【发布时间】:2021-08-12 03:22:02
【问题描述】:
在选择 Java 数据结构时,我正在为写入密集型应用程序寻找各种替代方案。我知道 ONE 数据结构无法为写入密集型应用程序提供单一的通用解决方案,但我对缺乏关于该主题的讨论感到惊讶。
很多人都在谈论read-intensive-rate-writes 或concurrent-read-only 应用程序,但我找不到任何关于用于写入密集型应用程序的数据结构的讨论。
基于以下要求
- 键/值对 -
Map - 未排序 - 为简单起见
- 每分钟超过 1000 次写入 / 可忽略不计的读取
- 所有数据都存储在内存中
我正在考虑以下方法
- 简单的
ConcurrentHashMap:虽然基于 Oracle 官方文档中的这一点
[...] 尽管所有操作都是线程安全的,但检索操作并不需要锁定
它必须更适合读取密集型应用程序
-
BlockingQueue和一组ConcurrentHashMaps 的组合。在批处理中,队列中的所有元素都被排空,然后在底层映射中适当地分配更新。在这种方法中,虽然我需要一个额外的地图来识别每个地图中包含哪些地图 - 就像一个协调器一样 - 使用
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