【问题标题】:Java Fast Data Storage & RetrievalJava 快速数据存储和检索
【发布时间】:2009-10-15 14:01:57
【问题描述】:

我需要将记录存储到持久存储中并按需检索。要求如下:

  1. 检索和插入速度极快
  2. 每条记录都有一个唯一的键。该密钥将用于检索记录
  3. 存储的数据应该是持久的,即应该在 JVM 重新启动时可用
  4. 一个单独的过程会每天将过时的记录移动到 RDBMS 一次

你们怎么看?由于延迟问题,我无法使用标准数据库。像 HSQLDB/H2 这样的内存数据库有性能约束。此外,记录是简单的字符串对象,不符合 SQL 条件。我正在考虑某种基于平面文件的解决方案。有任何想法吗?有什么开源项目吗?我敢肯定,以前一定有人解决过这个问题。

【问题讨论】:

  • “极快”是什么意思?
  • 亚毫秒级的存储和检索延迟
  • 您的读写比率是多少?阅读时,访问的模式是什么(随机的,块状的,...)?每条记录的唯一键的性质是什么(无所谓,uuid,时间戳)?
  • 您将很难获得亚毫秒级的任何东西 - 我知道有些人使用核心交易系统,他们为交易中低于 5 毫秒的端到端延迟感到自豪。这些要求对我来说似乎很模糊,细节也太少了。

标签: java


【解决方案1】:

有很多不同的工具和方法,但我认为它们都不能满足所有需求。

对于低延迟,您只能依靠内存中的数据访问 - 磁盘的物理速度太慢(SSD 也是如此)。如果数据不适合单个机器的内存,我们必须将我们的数据分发到更多的节点,从而总结出足够的内存。

为了持久性,我们毕竟必须将数据写入磁盘。假设最优组织 这可以作为后台活动完成,不会影响延迟。 但是对于可靠性(故障转移、HA 或其他),磁盘操作不能完全独立于访问方法:我们必须在修改数据时等待磁盘,以确保我们的操作不会消失。 并发还会增加一些复杂性和延迟。

数据模型在这里没有限制:大多数方法都支持基于唯一键的访问。

我们必须做出决定,

  • 如果数据适合一台机器的内存,或者我们必须找到分布式解决方案,
  • 如果并发是一个问题,或者没有并行操作,
  • 如果可靠性要求严格,我们不能松懈修改,否则我们可以忍受意外崩溃会导致数据丢失的事实。

解决方案可能是

  • 自行实现的数据结构使用标准 java 库、文件等可能不是最佳解决方案,因为可靠性和低延迟需要巧妙的实现和大量测试,
  • 传统的 RDBMS 具有灵活的数据模型、持久的、原子的和隔离的操作、缓存等 - 它们实际上知道的太多,而且大多难以分发。这就是它们太慢的原因,如果您无法关闭不需要的功能(通常是这种情况)。
  • NoSQL键值存储 是不错的选择。这些术语非常模糊,涵盖了很多工具。例子是
    • BerkeleyDB 或 Kyoto Cabinet 作为单机持久键值存储(使用 B 树):如果数据集足够小以适合一台机器的内存,则可以使用。
    • Project Voldemort 作为分布式键值存储:内部使用 BerkeleyDB java 版本,简单且分布式,
    • ScalienDB 作为分布式键值存储:可靠,但写入速度也不会太慢。
    • MemcacheDB、Redis 其他具有持久性的缓存数据库,
    • Cassandra、CouchDB、HBase 等流行的 NoSQL 系统:主要用于大数据。

可以找到 NoSQL 工具的列表,例如。 here

Voldemort 的performance tests 报告了亚毫秒级的响应时间,这可以很容易地实现,但是我们也必须小心硬件(如上面提到的网络属性)。

【讨论】:

    【解决方案2】:

    【讨论】:

      【解决方案3】:

      如果所有数据都适合内存,MySQL 可以在内存中而不是在磁盘中运行(MySQL 集群、混合存储)。然后它可以为您处理将自己存储到磁盘。

      【讨论】:

        【解决方案4】:

        CouchDB 之类的呢?

        【讨论】:

          【解决方案5】:

          我会为此使用 BlockingQueue简单,内置于 Java 中
          我使用来自芝加哥商品交易所的实时数据做了类似的事情。
          数据被发送到一个地方以供实时使用......并发送到另一个地方(通过 TCP), 使用 BlockingQueue(生产者/消费者)将数据持久保存到数据库(Oracle、H2)。
          消费者使用延时提交来避免数据库中的fdisk同步问题
          (H2 类型数据库默认是异步提交并避免该问题) 我在消费者中记录持久性以确保跟踪队列大小
          它能够跟上生产者的步伐。对我来说效果很好。

          【讨论】:

            【解决方案6】:

            带有分片的 MySQL 可能是个好主意。但是,这取决于您需要的数据量、每秒事务数和延迟。

            内存数据库也是一个好主意。事实上 MySQL 也提供了基于内存的表。

            【讨论】:

            • 是的...内存数据库很好...但是我以前使用 HSQLDB 的经验不是很好...事实上我们已经确定 HQSQL db 在我们的处理中花费了大量时间...虽然不确定 MSQL
            【解决方案7】:

            Tuple space / JavaSpace 有用吗?还可以查看其他企业数据结构,例如 Oracle CoherenceGemstone

            【讨论】:

              【解决方案8】:

              您是否真的证明了使用像 MySQL 或 SQL Server 这样的进程外 SQL 数据库太慢了,或者这是一个假设?

              您可以将 SQL 数据库方法与内存缓存结合使用,以确保检索根本不会命中数据库。尽管记录是纯文本的,但我仍然建议在平面文件解决方案上使用 SQL(例如,在表模式中使用文本列),因为 RDBMS 将执行文件系统无法执行的优化(例如,缓存最近访问的页面等) .

              但是,如果没有有关您的访问模式、预期吞吐量等的更多信息,我无法提供更多建议。

              【讨论】:

              • 是的。我们的遗留系统使用 RDBMS,检索数据需要几毫秒。这是高频应用,整个消息处理所需的亚毫秒级速度,其中存储和检索只是消息处理的一部分
              • 更重要的是,您的访问模式是什么?数据是顺序的(例如时间序列)吗?数据是一次写入多次读取,还是可能被更新?对此有定制的解决方案(例如 KDB),但这在很大程度上取决于您的用例。
              【解决方案9】:

              如果您正在寻找一个简单的键值存储并且不需要复杂的 sql 查询,Berkeley DB 可能值得一看。

              另一种选择是Tokyo Cabinet,一种现代 DBM 实现。

              【讨论】:

                【解决方案10】:

                如果在崩溃的情况下丢失了几个条目会有多糟糕?

                如果还不错,以下方法可能对您有用:

                为每个条目创建平面文件,文件名等于 id。可能一个文件的连续条目数量不多。

                确保您的控制器具有良好的缓存和/或使用以 Java 实现的现有缓存之一。

                与文件系统专家讨论如何真正加快速度

                这很简单,而且可能很快。 当然,您会丢失包括 ACID 原则在内的交易。

                【讨论】:

                • 可靠性要求相当高。我们不能在崩溃时丢失任何数据...
                【解决方案11】:

                亚毫秒读/写意味着你不能依赖磁盘,你必须小心网络延迟。忘记基于标准 SQL 的解决方案,不管是否是主内存。在一毫秒内,您不能通过 GBit 网络获得超过 100 KB 的数据。问问电信工程师,他们习惯于解决这类问题。

                【讨论】:

                  【解决方案12】:

                  如果你失去一两个记录有什么关系?他们来自哪里?您与来源有交易关系吗?

                  如果您有严格的可靠性要求,那么我认为您可能需要准备支付一些 DB 开销。

                  也许您可以将持久性问题与内存问题分开。使用 pup-sub 方法。一个订阅者负责内存中,另一个保存数据以备后续启动?

                  如果您可以购买而不是构建,那么诸如 WebSphere eXtreme Scale(不依赖 Java EE)之类的分布式 cahcing 产品可能是相关的。

                  【讨论】:

                  • 可靠性要求相当高。我也倾向于一些缓存解决方案。 EHCache?
                  【解决方案13】:

                  MapDB 提供了持久化到磁盘的高性能 HashMaps/TreeMaps。它是一个可以嵌入到 Java 程序中的单一库。

                  【讨论】:

                    【解决方案14】:

                    Chronicle Map 是一个ConcurrentMap 实现,它将键和值存储在堆外的内存映射文件中。所以你对 JVM 重启有持久性。

                    ChronicleMap.get() 始终快于 1 us,有时快至 100 ns/操作。是the fastest 课堂上的解决方案。

                    【讨论】:

                      【解决方案15】:

                      您需要的所有记录和键是否可以一次放入内存中?如果是这样,您可以只使用 HashMap,因为它是可序列化的。

                      【讨论】:

                      • -1 来自我。您需要在每次插入时手动序列化整个 HashMap,这显然非常慢。
                      • 是的...但是实时数据持久性怎么样?我需要保留数据,以便在 JVM 崩溃时不会丢失数据...
                      • @AAK:您可以序列化并存储每个更改。这样您就没有立即可用的持久性存储,但有一个日志,您可以在发生错误时重建存储。
                      • (顺便说一句,我并不是说这是理想的解决方案,我只是说它可以工作)
                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-07-13
                      • 1970-01-01
                      • 2014-07-24
                      相关资源
                      最近更新 更多