【问题标题】:Implementing a lock free data structure on disk在磁盘上实现无锁数据结构
【发布时间】:2013-12-03 05:35:43
【问题描述】:

我有一个有趣的挑战,对于那些在无锁数据结构和基于磁盘的数据结构方面具有深厚背景的人。

我正在寻找一种在 C++ 中构建数据结构以保存不同数量对象的方法。

限制是这样的:

  1. 数据结构必须驻留在磁盘上。
  2. 有一个线程写入数据结构,许多其他线程从中读取。
  3. 每次读取都是原子的。 (假设我可以为此自动读取大小为 32/64KB 的块,并且所有对象的大小都小于该大小。
  4. 写入不应阻塞读取,因为可以假设我也可以以原子方式写入 32/64KB 的块。
  5. 锁根本不能用。

有什么建议吗?

我正在考虑使用 B-Tree 之类的东西,当需要拆分节点并写入新数据而不是将它们移动到文件末尾的新节点,然后只需更新指向将驻留的节点的指针,例如在其他文件中(原始块将被标记为免费并添加到免费存储中)

但是,如果我的映射文件大于 32/64Kb,我会遇到问题。假设我希望它只保存 100 万个对象指针,而不是 4 字节/指针我得到 400 万字节,大约是4 Megs...(还有 10 亿个对象甚至更多...)这意味着映射文件不能以原子方式写入。

因此,如果有人对如何实现上述内容有更好的建议 - 甚至是某个方向,将不胜感激。

据我所知,B-Tree 的所有开源/商业实现都使用某种类型的锁,我无法使用。

谢谢, 最大。

【问题讨论】:

  • 但是你真的需要写整个地图文件吗?仅更新已更改的块还不够吗?
  • 我看不出没有锁你怎么能做到这一点......
  • 这在您描述的方式中是不可能的,因为 OS 文件访问 API 隐式具有读写器类型的锁定 - 即您基本上使用 OS 的互斥锁而不是您自己的。
  • 正如@DarkWanderer 所说,操作系统将使用锁来防止对磁盘的并发访问。您可能应该使用内存映射文件并查看无锁 B-Tree 实现。 cs.umanitoba.ca/~hacamero/Research/BtreeTechrpt2011.pdf 读起来很有趣。另外,出于好奇,为什么禁止使用锁?
  • 我无法在同一个句子中理解“无锁”和“磁盘”。

标签: c++ multithreading data-structures b-tree lock-free


【解决方案1】:

仅仅假设读/写是原子的,你不会走得太远——主要是因为它们不是,你最终会以一种会降低性能的方式模拟它。

听起来您想研究MVCC,这是设计无锁数据库时使用的非常标准的机制。基本概念是每次读取都会获得数据库的“快照”——通常以无锁方式实现,即不理会旧页面并仅对新页面执行任何修改。一旦旧页面被读者使用完毕,它们最终会被标记为可重复使用。

虽然 MVCC 比 CPU/RAM 无锁结构更复杂,但一旦你拥有它,许多相同的乐观无锁模式都适用于使用它。

【讨论】:

  • 如上所述,为了设计,假设在我的文件系统中它们是原子的。 MVVC 看起来很有希望,我将不得不对其进行更多研究,看看我如何将这个概念应用于我的磁盘存储,而不是内存存储。
  • 实际上 MVCC 并没有解决我的问题,因为我的数据结构需要可排序 - 因此允许对数据执行下一个/上一个操作。 MVCC 解决了一个可哈希表的问题,但它不提供排序..
  • MVCC 除了提供无锁操作外,与您的数据结构没有任何关系。它通常与数据库中的 B+树一起使用,但实际上可以用于任何事情。
  • 我或多或少地理解这一点,但据我所知,将 MVCC 与我正在尝试做的事情一起使用需要巨大的开销。 (如果您必须为 B+ 树中的每一跳重复对象版本控制过程,这是低效的)
  • 此外,我正在尝试优化我的读/写原子性,如果最终 32/64KB 写入是原子的,这意味着我可以尝试缓存大部分树映射以提高性能。 (例如,如果使用 b+ 树而不是 b-tree,我可以在一次读取中缓存对相当多成员的引用以优化性能)
【解决方案2】:

LMDB 将毫无问题地完成所有这些工作。它是一个 MVCC B+树,读者是完全无锁的。

【讨论】:

    猜你喜欢
    • 2017-03-30
    • 2015-09-09
    • 2012-09-19
    • 2011-04-12
    • 1970-01-01
    • 2015-10-13
    • 1970-01-01
    • 2011-01-09
    • 1970-01-01
    相关资源
    最近更新 更多