用于存储记录的数据库/缓存的纯 Java 替代方案

Pure Java alternative to database / cache for storing records

我创建了一个销售给客户的应用程序,其中一些是具有固定约束的硬件制造商(慢CPU)。该应用程序必须在 java 中,以便可以将其作为单个软件包轻松安装。

该应用程序是多线程的并维护音频记录。在这种特殊情况下,我们所拥有的只是 INSERT SOMEDATA FOR RECORD,每条记录代表一个音频文件(这可以由不同的线程完成),然后我们有 SELECT SOMEDATA WHERE IDS in (x, y, z) 通过一个线程,然后第三步是我们实际上删除了这个 table.

中的所有数据

主要约束是cpu,慢单cpu。内存也是一个限制,但只是因为应用程序的设计使其可以处理无限数量的文件,因此即使有很多内存,如果全部存储在内存中而不是利用磁盘,最终也会 运行 耗尽.

在我的 Java 应用程序中,我开始使用 H2 数据库来存储我的所有数据。但是该软件必须 运行 在一些速度较慢的单个 cpu 服务器上,所以我想减少使用的 cpu 周期,我想再次查看的一个领域是数据库。

在很多情况下,我将数据插入数据库只是为了将数据保持在堆之外,否则会 运行 内存不足,然后稍后我们检索数据,我们永远不必更新数据。

所以我考虑使用像 ehCache 这样的缓存,但是有两个问题:

解决这些问题的替代方案是什么?

一些可以提供帮助的想法

  1. 您说您 运行 在一个 CPU 上并且想要检查对 H2 的替换。因此,H2 "consumes" 有很多 CPU 的功能,并且应用程序声称是 "slow"。但是,如果是因为磁盘速度慢而不是 CPU 怎么办,毕竟,数据库将其内容存储在磁盘上,而磁盘可能很慢。如果你想验证这个理论 - 将磁盘映射到某个 ram 支持的驱动器(在 linux 中这是一个简单的任务)并使用相同的 CPU.

  2. 再次测量
  3. 如果您得出的结论是 H2 确实 CPU 密集用例,也许值得投入一些时间来优化查询,这比替换数据库便宜得多。

  4. 现在,如果您不能继续使用 H2,请考虑针对此 "append-only" 用例真正优化的 Lucene(我知道您有 "append-only" 流程,因为你说 "later on we retrieve the data, we never have to UPDATE the data). Having said that Lucene also should have its own threads that handle indexing, so some CPU overhead is expected anyway. However, the chances are that Lucene will be faster for this use case. The price is that you won't get "easy" 查询,因为 lucene 没有实现关系模型(好吧,可能部分是因为它应该更快),特别是你不会有 JOINs 和事务管理。它可以查询根据来自单个 table 的条件,就像在 RDMBS 中一样,您不必像您描述的那样获得 "top hits"。

根据您的问题和对 Mark Bramniks 回答的评论,我了解到:

  • CPU 约束:非常慢 cpu,解决方案不应 cpu 密集
  • 内存限制:并非所有数据都可以在内存中
  • 磁盘限制:磁盘非常慢,解决方案不应read/write来自磁盘的大量数据

这些是非常严格的限制。通常你 "trade" cpu vs 内存或内存 vs 磁盘。在您的情况下,这些都是约束。你提到你看过 ehCache,但我认为这个解决方案(可能还有其他解决方案,如 memcached)并不比 H2 更轻量级。

您可以尝试的一种解决方案是 MappedByteBuffer。 class 使得在内存中保存文件的一部分成为可能,并在需要时交换这些部分。但这是有代价的,它不是一头容易驯服的野兽。您将需要编写自己的算法来定位所需的数据。请考虑您需要多少时间才能使其正常工作,以及购买更大机器的额外成本。有时更好的硬件是解决方案。

Oracle这样的关系数据库已经有几十年的历史了(41年),你能想象当时有多少CPU个周期可用吗?基于 1970 年的研究并为专业人士所熟知,经过测试、记录、可靠、一致(校验和)、可维护(零数据丢失的备份)、如果使用正确(各种索引)、可通过网络安全访问、可扩展、等等,但显然不是这里发明的。

现在甚至有许多像 PostgreSQL 这样的免费开源数据库,它们的要求非常适中,并且有可能在未来轻松实现新的要求(这很难预测),并且通过一些努力可以与其他数据库互换(JDBC, JPA)

但是,是的,有一些开销,但通常硬件比在项目后期更改架构更便宜,而且 CPU 周期不再是昂贵的资源(想想 raspberry pi、智能手机等)

您想快速批量检索记录,不丢失任何数据,但您不需要优化查询或更新,并且您希望尽可能有效地使用 CPU 和内存资源:

为什么不直接将记录存储在文件中?操作系统使用任何空闲内存进行缓存。因此,当您频繁访问文件时,OS 将尽最大努力在内存中保留尽可能多的内容。 OS 无论如何都会完成这项工作,因此这种类型的缓存不会花费您额外的 CPU 并且不需要一行代码。

唯一可以在优化方面投入更多资金的场景是:

  • a) 您的进程或其他进程大量使用文件系统并且 污染文件缓存
  • b) 序列化/反序列化太昂贵

如果是 a):

确定您的优先事项。显式缓存(在堆中或堆外)可以帮助您将选定文件的某些内容保留在内存中。但此内存将不再可用于 OS 的文件缓存。因此,当您加快一个文件的访问速度时,您可能会减慢对其他文件的访问速度。

如果是 b):

在优化任何东西之前先测量性能。通常磁盘访问是瓶颈——如果不更换硬件就无法改变这一点。如果您仍想优化(例如,由于大量临时创建的对象导致 GC 耗尽 CPU - 我猜只有一个核心串行 GC 将被使用)那么我建议仔细查看Google flatbuffers

您从最复杂的问题解决方案开始,即数据库。我建议从另一端开始,尽可能简单


更新: 同时对问题进行了编辑,要求也发生了变化。现在的新要求是必须可以通过 ID 读取选定的记录。

可能的扩展:

  • 将每条记录存储在自己的文件中,并使用密钥作为文件名
  • 将所有记录存储在一个文件中,并使用基于文件的 HashMap 实现 喜欢 MapDB's HTreeMap 实现。

独立于所选扩展名,操作系统的文件缓存将尽最大努力在主内存中保存尽可能多的内容。