我们习惯于将数据库视为巨大的存储平台,我们可以在其中存储我们需要的所有数据,然后通过某种形式的查询语言检索它。扩展这些数据库,保持信息的一致性和容错性本身就是一个挑战。但是,当我们的数据需求非常小时会发生什么?
当RedShift、BigQuery、甚至MySQL对我们微小的数据需求来说是一个太大的解决方案时,会发生什么?好吧,事实证明,有一个应用程序可以解决这个问题。事实上,有很多选择,所以在这里,我将介绍5大嵌入式数据库,以满足你的微小数据需求。
什么是嵌入式数据库?
当我们读到“嵌入式”这个词时,90% 的人会得出结论,我说的是物联网或移动设备。但这种情况并非如此。
反正不是唯一的情况。诚然,这些系统的资源非常有限,这使得大多数传统的数据库系统很难配置和安装在那里。
但是对于小型数据库还有其他的用例,也就是将它们嵌入到软件产品中。例如,想象一下通过您的IDE在一个大型代码存储库上进行搜索。IDE可以嵌入一个反向索引数据库,允许您搜索关键字并快速获取相关文件的参考。或者在您最喜欢的电子邮件桌面客户端上执行搜索时,该客户端很可能也有一个嵌入式数据库。所有的电子邮件都存储在那里并被编入索引,所以你可以快速轻松地访问这些信息。
到目前为止,您可能已经了解了嵌入式数据库的另一个巨大好处,即它们不需要与网络调用进行交互。与标准数据库相比,这是一个巨大的性能提升。从本质上讲,在正常的开发中,你希望把数据库放在自己的服务器(或服务器集群)上,这样它的资源消耗就不会影响到你架构中的其他组件,而对于嵌入式数据库,你希望它们尽可能地靠近客户端代码。这就减少了它们之间的延迟,避免了对通信渠道(即网络)的依赖。
现在,这个想法可以有多种形式,从使用 JSON 文件作为主存储的快速内存数据库,到可以使用类似 SQL 的语言进行查询的高效微型关系数据库。
让我们来看看5种选择。
LowDB
让我们从简单的开始,LowDB 是一个小型的内存数据库。这是一个非常基本的解决方案,但它解决了一个非常简单的用例:需要从基于 JavaScript 的项目中存储和访问类似 JSON 的结构(即文档)。
LowDB的一个主要好处是它可以从JavaScript中使用,也就是说:它可以用于后端、桌面和浏览器代码。
在后端,你可以将它与 Node.js 一起使用,对于桌面开发,它可以集成到一个 Electron 项目中,最后,它也可以通过其集成的 JS 运行时直接在浏览器上运行。
这个数据库提供的API也相当简单和简约,它不提供任何开箱即用的搜索功能。它只限于将一个JSON文件的数据加载到一个数组变量中,让你(用户)以你认为合适的方式找到你要找的东西。
例如,看看下面的代码:
- import { LowSync, JSONFileSync } from 'lowdb'
- const title = "This is a test"
- const adapter = new JSONFileSync('file.json')
- const db = new LowSync(adapter)
- db.read() //将文件内容加载到内存中
- db.data ||= { posts: [] } //默认值
- db.data.posts.push({ title }) //将数据添加到“集合”中
- db.write() //通过将数据保存到JSON文件来持久化数据
- //任何类似于查找的操作都由用户自己来完成
- let record = db.data.posts.find( p => p.title == "Hello world")
- if(!record) {
- console.log("No data found!")
- } else {
- console.log("== Record found ==")
- console.log(record)
- }
正如你所看到的,这里有趣的部分不是默认的行为,而是我正在使用一个叫做 JSONFileSync 的适配器。我可以很容易地使用一个由我创建的自定义的适配器,这才是这个数据库的真正强项。
它具有高度的可扩展性并与 TypeScript 兼容,后者为数据存储提供了类似于模式的行为(即不允许添加不遵循预设模式的数据)。
如果混合使用这两种选项,那么LowDB将成为处理本地类似json的数据的有趣选项。
LevelDB
LevelDB 是由 Google 构建的开源键值数据库。它是一种超快但非常有限的键值存储,其中数据按开箱即用的键排序存储。
它只有三个基本操作:Put、Get 和 Delete,没有别的——如果你仔细想想,有点像 LowDB。
和LowDB一样,它没有一个客户端-服务器封装器,这意味着没有办法从任何语言与它通信,如果你想使用它,你必须使用C/C++库,如果你想要一个类似服务器的行为,你必须自己封装它。
就像我们在这里要介绍的大多数案例一样,功能非常基本,因为它们涵盖了一个非常简单但需要的用例:在靠近代码的地方存储数据并快速访问。
该数据库的存储架构是围绕着日志结构的合并树(LSM),这意味着它被优化为大型的连续写操作,而不是小型的随机操作。
LevelDB 的一个主要限制是,一旦打开,它就会在存储上获得系统级锁,这意味着当时只有一个进程可以与数据库交互。当然,您可以使用多个线程来并行化该进程中的某些操作。但这就是它的范围。
有趣的是,这个数据库被用作Chrome的IndexedDB的后台数据库,显然Minecraft Bedrock版也使用它来存储一些分块和实体数据(尽管从外观上看,他们使用的是谷歌实现的略微修改的版本)。
Raima 数据库管理器
我之前提到过物联网不是吗? Raima 是速度最快的数据库管理器之一,专门针对在资源受限的物联网设备中运行进行了优化。
资源受限的环境是什么意思? Raima 只需要 350kb 的 RAM 即可运行。这就是我可以极简的资源利用。
该解决方案的主要特点之一是它完全支持 SQL,这一点在之前的任何解决方案中都没有出现。它提供了一个关系数据模型,并允许您使用 SQL 语言进行查询。
与 LevelDB 不同的是,它还允许通过客户端-服务器架构对数据库进行多进程访问(即这种架构允许您比其他架构更远离源代码)。如果您决定采用接近源代码的嵌入式应用程序,您还可以使用多线程来支持对多个数据库的并发访问。
Raima的灵活性允许你从传统的客户-服务器方法到最有效的(当然也是有限的)使用案例,即由单个客户消费的单一内存数据库。但是,嘿,这是一个非常有效的嵌入式数据库的使用案例。
这种灵活性使它成为一种非常通用的解决方案。当然,每种部署模式都有自己的优点和限制,但是也会针对特定的用例进行优化。因此,请确保您选择了正确的一个,并从这个数据库中获得最大的好处。
Apache Derby
如果你正在寻找另一个非常小的、类似SQL的数据库,Apache Derby很可能是你正在寻找的东西。
Derby完全是用JAVA编写的,当它声称只有3.5Mb的内存占用时,也损失了一点信誉。毕竟,如果没有在主机系统上安装JVM,您就不能运行或使用它。
也就是说,如果您的用例允许使用JVM,那么很好,您可以继续考虑Derby,否则您可能希望使用更本地的解决方案,如LevelDb或Raima。
但正如我所说,如果您已经在从事 JAVA 项目并且需要集成一个小型、可靠、基于 SQL 的数据库,那么 Derby 绝对是一个潜在的候选者。
它带有一个集成的 JDBC 驱动程序,因此不需要额外的依赖项。它既可以在 JAVA 应用程序内的嵌入式模式下工作,也可以作为独立服务器运行,允许多个应用程序同时与其交互(类似于 Raima 的工作方式,但没有许多变体)。
说实话,这个项目最大的缺点是它的文档。它可能是JAVA社区的一个标准,但它对用户并不友好,大部分的官方链接都把读者送到一个私人的汇合页面。这里的许多其他解决方案在涉及到文档时提供了更顺畅的体验,这也有助于他们产品的采用。
solidDB
最后,solidDB提供了一个非常有趣的内存关系数据库,它同时还可以增强一个持久性模型。声称它可以保持两个数据存储选项实时同步。这可不是个小要求。
本质上就像这里列出的其他解决方案一样,solidDB 可以通过 ODBC 或 JDBC 访问,这允许 JAVA 和 C 应用程序通过 SQL 与其交互。
也像这里列出的一些解决方案一样,它可以以多种模式部署:
- 高可用性模式。这涉及具有重复数据的多个服务器。当然,这种模式在我们考虑的用例中并不多。
- 共享内存访问。这个方案非常有趣,因为它不仅将数据保存在内存中(就像已经列出的其他解决方案一样),而且还允许多个应用程序访问该内存(因此有共享内存部分)。当然,对共享内存的直接访问需要由同一节点内的应用程序完成,但是,它还允许基于 JDBC/ODBC 从外部节点访问相同的数据。将共享内存转变为具有外部访问权限的内存数据库。
由于访问数据的速度快如闪电,思科、阿尔卡特、诺基亚和西门子等多家知名企业声称将使用该数据库进行关键任务操作。
鉴于其所有的部署模式、广泛的文档和高需求的客户名单,我可以看到这是这个名单上最可靠、最稳定、最快速的嵌入式数据库之一。
嵌入式数据库是为了处理一个非常具体的用例,或者通过提供快速可靠的数据存储和最小的延迟,或者通过允许快速安全地访问数据。这里列出的解决方案通过不同的手段实现这些目标,这取决于你和你的特定环境,以决定哪一个是适合你的。