数据库索引的排序字符串表(SSTable)或B +树?

时间:2011-12-28 02:47:59

标签: database indexing nosql couchdb cassandra

使用两个数据库来说明此示例:CouchDBCassandra

CouchDB的

CouchDB使用B + Tree作为文档索引(使用a clever modification在其仅附加环境中工作) - 更具体地说,当文档被修改(插入/更新/删除)时,它们被附加到正在运行的数据库文件中以及完整的叶 - >来自所有节点的B +树的节点路径受到文档之后的更新版本的影响。

这些分段修改的索引修订版本与修改一起内联,使得完整索引是附加在文件末尾的最新索引修改的联合,以及在数据文件中进一步返回的仍然相关的其他部分并且尚未修改。

  

搜索B+ tree 是O(登录)。

卡桑德拉

Cassandra将记录键在内存中保存在表中(让我们将它们视为此问题的数组),并将它们作为单独的(已排序的)sorted-string tables不时写出来。

我们可以将所有这些表的集合视为“索引”(根据我的理解)。

Cassandra有时需要compact/combine these sorted-string tables,创建索引的更完整的文件表示。

  

搜索a sorted array是O(登录)。

问题

假设在CouchDB中维护部分B +树块与Cassandra中的部分排序字符串索引之间存在类似的复杂程度,并且假设两者都提供O(logn)搜索时间,您认为哪一个可以更好地表示数据库索引和为什么?

我特别好奇是否有一个关于一个的实现细节使其特别有吸引力或者如果它们都是一个洗,你只需选择你喜欢使用的数据结构/对开发者来说更​​有意义。

感谢您的想法。

4 个答案:

答案 0 :(得分:51)

将BTree索引与SSTable索引进行比较时,应考虑写入复杂性:

  • 当写入随机写入BTree时,您将进行随机读取(以执行叶节点和路径的复制)。因此,虽然写入顺序在磁盘上,但对于大于RAM的数据集,这些随机读取将很快成为瓶颈。对于类似SSTable的索引,写入时不会发生此类读取 - 只会进行顺序写入。

  • 您还应该考虑在更糟糕的情况下,对BTree的每次更新都可能产生log_b N IO - 也就是说,您最终可能会为每个密钥写入3或4个块。如果密钥大小远小于块大小,则这非常昂贵。对于类似SSTable的索引,每个写入IO将包含尽可能多的新密钥,因此每个密钥的IO成本更像是1 / B.

在实践中,这使得SSTable比BTree快数千倍(对于随机写入)。

在考虑实现细节时,我们发现实现类似SSTable的索引(几乎)无锁定要容易得多,因为BTrees的锁定策略变得非常复杂。

您还应该重新考虑您的阅读费用。对于随机点读取,你是正确的,而不是BTree是O(log_b N)随机IO,但是类似SSTable的索引实际上是O(#sstables.log_b N)。如果没有合适的合并方案,#sstables与N成正比。有各种各样的技巧可以解决这个问题(例如使用布隆过滤器),但这些对小型随机范围查询没有帮助。这就是我们在Cassandra中找到的:

http://www.acunu.com/blogs/richard-low/cassandra-under-heavy-write-load-part-ii/

这就是为什么Castle,我们的(GPL)存储引擎确实合并稍有不同,并且可以在写入性能略有折衷的情况下实现更好的(O(log ^ 2 N))范围查询性能(O(log) ^ 2 N / B))。在实践中,我们发现它比Cassandra的SSTable写入索引更快。

如果您想了解更多相关信息,我已经谈过它的工作原理:

答案 1 :(得分:9)

我认为Tokutek使用的分形树是数据库的更好索引。与b树相比,它们提供了20到80倍的实际改进。

关于分形树索引如何工作here有很好的解释。

答案 2 :(得分:1)

LSM-Trees在存储引擎结构上优于B-Trees。 它以某种方式将随机写入转换为aof。 这是一个LSM-Tree src: https://github.com/shuttler/lsmtree

答案 3 :(得分:1)

每种方法都应该提到一些事项:

B树

  • 读/写操作应该是对数O(logn)。但是,单个数据库写入可能会导致存储系统中的多次写入。例如,当节点已满时,必须将其拆分,这意味着将有2个写入用于2个新节点,1个额外写入用于更新父节点。如果父节点也已满,您可以看到它会如何增加。
  • 通常,B树以每个节点具有页面大小的方式存储。这会产生一种称为写入放大的现象,即使需要更新单个字节,也会写入整个页面。
  • 写入通常是随机的(非顺序),因此更慢,特别是对于磁盘。

SSTables

  • SSTable通常用于以下方法。如您所述,存在一种称为memtable的内存中结构。每隔一段时间,这个结构就被刷新到磁盘上一个SSTable。因此,所有写入都会转到memtable,但读取可能不在当前memtable中,在这种情况下,它们会在持久化的SSTables中搜索
  • 因此,写入为O(logn)。但是,要记住它们是在内存中完成的,因此它们应该比B树磁盘中的对数运算快几个数量级。为了完整起见,我们应该提到写入也写入预写日志以进行崩溃恢复。但是,鉴于这些都是顺序写入,预计它们比B树的随机写入更有效
  • 从内存中提供(来自memtable),读取预计会更快。但是,当需要查看较旧的基于磁盘的SSTable时,读取可能会比B树慢得多。有几个优化,例如使用bloom过滤器,来检查SSTable是否包含一个值而不执行磁盘读取。
  • 正如您所提到的,还有一个后台进程,称为压缩,用于合并SSTable。这有助于删除已删除的值并防止碎片,但它可能会导致显着的写入负载,从而影响传入操作的写入吞吐量。

显而易见,这两种方法之间的比较要复杂得多。在极其简化的尝试中提供具体的比较,我想我们可以这样说:

  • SSTables提供比B树更好的写入吞吐量。然而,由于持续的压缩,预计它们的行为会较不稳定。在this benchmark comparison
  • 中可以看到这方面的一个例子
  • 对于需要事务语义的用例,通常首选B树。这是因为,每个键只能在一个地方找到(与SSTable相反,它可能存在于多个SSTable中,其中一些中有一些过时的值),也因为一个可以表示一系列值作为树。这意味着更容易执行键级和范围级锁定机制。

参考

[1] A Performance Comparison of LevelDB and MySQL

[2] Designing Data-intensive Applications