【问题标题】:Java concurrent collection for write intensive application - only a few reads用于写入密集型应用程序的 Java 并发集合 - 只需少量读取
【发布时间】:2021-08-12 03:22:02
【问题描述】:

在选择 Java 数据结构时,我正在为写入密集型应用程序寻找各种替代方案。我知道 ONE 数据结构无法为写入密集型应用程序提供单一的通用解决方案,但我对缺乏关于该主题的讨论感到惊讶。

很多人都在谈论read-intensive-rate-writesconcurrent-read-only 应用程序,但我找不到任何关于用于写入密集型应用程序的数据结构的讨论。

基于以下要求

  1. 键/值对 - Map
  2. 未排序 - 为简单起见
  3. 每分钟超过 1000 次写入 / 可忽略不计的读取
  4. 所有数据都存储在内存中

我正在考虑以下方法

  1. 简单的ConcurrentHashMap:虽然基于 Oracle 官方文档中的这一点

[...] 尽管所有操作都是线程安全的,但检索操作并不需要锁定

它必须更适合读取密集型应用程序

  1. BlockingQueue 和一组ConcurrentHashMaps 的组合。在批处理中,队列中的所有元素都被排空,然后在底层映射中适当地分配更新。在这种方法中,虽然我需要一个额外的地图来识别每个地图中包含哪些地图 - 就像一个协调器一样
  2. 使用HashMap 并在 API 级别进行同步。这意味着每个与写入相关的方法都将被同步
synchronized void aWriteMethod(Integer aKey,String aValue) {
   thisWriteIntensiveMap.put(aKey,aValue);
}

如果这个问题不仅受到对上述选项的批评,而且还受到有关新的和更好的解决方案的建议,那就太好了。

PS:除了数据的完整性、操作顺序和节流问题之外,在为写入密集型选择“最佳”方法时还需要考虑哪些其他因素。

我知道这可能看起来有点开放,但听听人们对这个问题的看法会很有趣。

【问题讨论】:

  • 用例是什么?为什么地图?如果您有一个写入密集型应用程序并且不需要重复数据删除,您可以使用链表而不锁定整个结构。
  • @BurakSerdar 如果我使用地图,那么在每次更新/更新/删除时,我都需要扫描整个列表以找到我想要触摸的条目。这就是为什么地图似乎是更合适的选择
  • 另一点是,您需要了解例如插入操作的速度和算法,因为这样您就知道将使用哪种同步。例如。是否只有一个值被锁定,值的子集是否被锁定,或者整个集合是否被锁定。如果您考虑如何重新平衡树,我敢肯定这会产生巨大的同步开销。最后,它会因应用程序而异,因此负载测试/基准测试是找出最佳替代方案的好方法。
  • 您可能更喜欢使用NonBlockingHashMap 来实现无锁映射,它以不支持原子计算方法为代价提供更高的写入吞吐量。否则,如果您不需要立即读取写入,中间队列(通过环形缓冲区)允许您廉价地记录更新,在单个线程上耗尽它,并通过追赶来惩罚读者。 Caffeine 缓存使用这种记录/重放方法,因为每次 LRU 读取都是内部写入。
  • @Niko 是的,尽管ArrayBlockingQueue 使用锁,而 JCTools 提供有界无锁队列。无论哪种方式,都存在分片和排空队列的复杂性。您的 16+/s 写入速率很低,因为 ConcurrentHashMap 可以在 Zipfian 分布上执行50M writes/s。您应该通过 JMH 进行基准测试,因为您可能会对股票哈希图感到满意。

标签: java concurrency java.util.concurrent


【解决方案1】:

即使您会选择最糟糕的地图类型,例如一个同步的 HashMap,我认为您不会注意到负载对性能的任何影响。每分钟几千次写入不算什么。

我会设置一个 JMH 基准并尝试各种地图实现。显而易见的候选者是 ConcurrentHashMap,因为它旨在处理并发访问。

所以我认为这是过早优化的一个很好的例子。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-20
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    相关资源
    最近更新 更多