我想知道什么是支持并发写入的最佳数据结构,特别适合写密集型应用程序。数据结构应该是一个哈希映射,并且将基于磁盘。我想到的一个想法是提高并发级别,即拥有固定数量的散列桶,并在每个桶中,一个基于树的数据结构,用于存储键值对。但是我不确定要使用的基于树的数据结构。此外,我需要有关数据结构的信息,而不是一些提供并发哈希映射的库,因为我需要自己实现它,我们不能在内核空间中使用用户空间库。是否有任何数据结构允许记录级别锁定,以便在写入时我不会锁定整个数据结构。任何想法或建议? C将用作实现的语言。
答案 0 :(得分:1)
如果该表是基于磁盘的,那么您可能希望使用封闭的散列方案,例如: Hopscotch Hashing;这是因为开放式哈希方案通常需要链接数据结构,这对于(从)磁盘序列化更加困难。
然后,您可以通过sharding哈希表增加并发性并缓冲其写入。例如,您可以创建256个单独的哈希表,每个哈希表都在自己的文件中并锁定。
struct Shard {
Lock lock
File table
}
现在,您将创建一个包含256个分片和工作线程池的全局数组。您需要某种方式在线程之间均匀分配工作负载,例如,跳过当前执行昂贵操作的线程的循环线程队列,例如将队列刷新到磁盘。
struct WorkerThread {
Queue[] queues = Queue[256]
}
Shards[] shards = Shards[256]
WorkerThread[] threads = WorkerThread[8]
void processInput(WorkerThread thread, Data data) {
int modulus = hashValue(data) % 255
Queue queue = thread -> queues[modulus]
addToQueue(queue, data)
if(sizeOf(queue) > 64) {
Shard shard = shards[modulus]
lock(shard -> lock)
writeToDisk(shard -> file, queue)
unlock(shard -> lock)
}
}
每个线程维护输入数据的线程局部缓冲区,索引在 n%255 ;当缓冲区超过64个项目时,该线程会锁定相应的分片并将缓冲区刷新到磁盘。如果线程数明显小于分片数,那么你不应该遇到很多锁争用,如果线程本地缓冲区足够大,那么你不应该遇到很多磁盘争用
您需要保持碎片相对较小,以降低IO的成本,因为您可能在每次writeToDisk
操作期间读取和写入整个碎片文件。为了增加分片数量,您可以全局重新分片(例如,将分片数量从256增加到1024),这可能需要停止世界操作;一个更复杂但可能性能更高的替代方法是执行两个或更多级别的分片(因此第一级使用 n%255 来索引数据,然后如果分片变得太大而不是下一级别使用 n / 256%15 或类似的东西)