【问题标题】:Persistent (purely functional) Red-Black trees on disk performance磁盘性能上的持久(纯功能)红黑树
【发布时间】:2011-02-15 17:53:27
【问题描述】:

我正在研究最好的数据结构来实现一个简单的开源对象时态数据库,目前我非常喜欢使用 Persistent Red-Black 树来完成它。

我使用持久化数据结构的主要原因首先是尽量减少锁的使用,这样数据库可以尽可能的并行。此外,实现 ACID 事务将更容易,甚至能够将数据库抽象为在某种集群上并行工作。 这种方法的伟大之处在于它几乎可以免费实现时态数据库。这是非常好的东西,特别是对于网络和数据分析(例如趋势)。

所有这些都非常酷,但我对在磁盘上使用持久数据结构的整体性能有点怀疑。尽管现在有一些非常快的磁盘可用,并且所有的写入都可以异步完成,所以响应总是立即的,我不想在一个错误的前提下构建所有应用程序,只是意识到这并不是一个好的方法。

这是我的想法: - 由于所有写入都是异步完成的,并且使用持久数据结构不会使以前的 - 和当前有效的 - 结构无效,因此写入时间并不是真正的瓶颈。 - 有一些关于像this 这样的结构的文献,这些结构完全适合磁盘使用。但在我看来,这些技术会增加更多的读取开销以实现更快的写入。但我认为恰恰相反更可取。此外,许多这些技术确实最终会产生多版本树,但它们并不是严格不可变的,这对于证明持久开销的合理性非常重要。 - 我知道在将值附加到数据库时仍然需要某种锁定,并且我也知道如果不是要维护所有版本,应该有一个很好的垃圾收集逻辑(否则文件大小肯定会急剧增加) .也可以考虑使用增量压缩系统。 - 在所有搜索树结构中,我真的认为红黑是最接近我需要的,因为它们提供的旋转次数最少。

但在此过程中可能存在一些陷阱: - 异步写入 - 可能会影响需要实时数据的应用程序。但我认为大多数时候 Web 应用程序并非如此。此外,当需要实时数据时,可以设计另一种解决方案,例如需要以更实时的方式处理特定数据的签入/签出系统。 - 它们也可能导致一些提交冲突,尽管我想不出一个很好的例子来说明何时会发生。如果两个线程处理相同的数据,在正常的 RDBMS 中也会发生提交冲突,对吗? - 拥有这样一个不可变接口的开销将呈指数级增长,一切都注定很快就会失败,所以这一切都是个坏主意。

有什么想法吗?

谢谢!

编辑: 似乎对什么是持久数据结构存在误解: http://en.wikipedia.org/wiki/Persistent_data_structure

【问题讨论】:

  • 你能解释一下为什么“我使用持久数据结构的主要原因首先是尽量减少锁的使用”吗?持久与否,你仍然需要锁......
  • 嗯,你是对的。仍然需要使用锁,但它已降至最低限度。例如,在我的例子中,我们唯一需要锁的地方是“弱”引用,比如红黑树的头部。将所有树更改附加到文件后,我们必须锁定它,仅将指针(仅一个 int)更改为树的头部。不知道的读者不可能捕获处于不一致状态的树,并且锁应该非常快地工作。同样对于写入,唯一需要锁定的时间是更改文件的大小(向其追加数据)
  • 我刚刚意识到像 Okasaki 的书中建议的那种纯函数式方法并不是一个好的选择,因为不仅空间效率很差,而且更难查询一段时间,因为需要检查从一个版本到另一个版本的变化。
  • 也许保留提交日志,比如 git,可以解决这个问题
  • 您可能想查看this(一个功能性的、仅限附加的数据库)

标签: database data-structures functional-programming binary-tree immutability


【解决方案1】:

对志同道合的人很感兴趣 :-) 我实际上已经实现了一个使用持久数据结构作为其数据模型的数据库。一种持久性 B2 树,我想可以称之为。仅追加存储到磁盘和垃圾收集 - 并非所有历史记录都需要永久保存。可以设置一个有限的保留期,让数据库忘记早期历史。

http://bergdb.com/

【讨论】:

    【解决方案2】:

    我知道这个问题有点老了,但我一直在实施几乎相同的事情,我发现,作为二叉树意味着性能很糟糕(由于搜索次数) .尽管有额外的空间开销,但尝试制作更宽的持久树可能是一个更好的主意。

    【讨论】:

    • 你完全正确。实际上有一个很好的实现来照顾 - couchdb 的不可变 b-tree !但是现在我已经改变了这个项目的方向,我放弃了对磁盘上纯函数数据结构的需求,因为它们在这种情况下并不是很合适。对于无锁结构,最好在内存映射文件上实现 CAS 操作。
    • @Waneck,是的,我看过 couchdb 的 b-tree(虽然我还没有深入研究实现)。你介意解释你关于无锁结构的第二条评论吗?我不确定我是否理解。
    • 请看stackoverflow.com/questions/2846190/…!在我发现您可以对内存映射文件进行比较和交换操作之后,在我看来,持久数据结构对于数据库来说并不是一个很好的解决方案。仅追加意味着没有局部性,毕竟磁盘(性能方面)使用率很低。
    【解决方案3】:

    如果您发现写入时间遇到瓶颈,或者如果没有同步写入,您的持久性保证毫无意义(嗯...),您应该执行大多数其他数据库所做的事情:实现 Write-Ahead Log (WAL),或者重做日志。

    磁盘实际上非常擅长按顺序写入,或者至少这是它们最擅长的。这是非常慢的随机写入(例如树中的那些)。即使是在随机写入方面击败磁盘的闪存驱动器,在顺序写入方面仍然要好得多。实际上,即使是大多数 RAM 也更擅长顺序写入,因为涉及的控制信号更少。

    通过使用预写日志,您不必担心:

    • Torn writes(你在猫吃掉你的电源之前写了半个树的图像)
    • 信息丢失(您实际上并没有持久化树,但乔认为您做到了)
    • 随机同步磁盘 I/O 对性能造成巨大影响。

    【讨论】:

    • 嘿!谢谢你的提示!这确实是必须的,因为使用的内存很容易变满。但是在数据库是完全临时的(所有更改的数据都被记录)的情况下,它实际上可能只是一个文件!从这个意义上说,垃圾收集是我认为最大(但必要)的减速之一。
    • 您能解释一下为什么使用 WAL 意味着您不必担心“随机同步磁盘 I/O 会造成巨大的性能损失”吗?
    • 预写式日志很少随机读取/写入数据;它始终是附加到的,这在传统硬盘上速度很快。是的,系统中还会有其他随机写入,但对于基本上每条记录更新都会用到的东西,效率很重要。
    • 是的,您不必担心写入日志的性能问题,但您的答案可能会被解读为根本不必担心读/写时的性能问题。
    【解决方案4】:

    我的想法是你有一个好主意。现在去建造该死的东西。从您所写的所有内容来看,听起来您正遭受analysis paralysis 的急性病例。

    【讨论】:

    • 嘿!我真的很高兴你这么认为!我已经在编写它了,但是由于这是我第一次编写 DBMS,我想也许我可能在某个地方走错了方向!谢谢!
    猜你喜欢
    • 2016-12-03
    • 1970-01-01
    • 2015-08-06
    • 1970-01-01
    • 1970-01-01
    • 2016-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多