tonglin0325的个人主页

存储底层数据结构对比

该文章对比了常用的一些存储底层所使用的数据结构。

1.B+树#

MySQL,MongoDB的索引使用的就是B+树

B+树在多读少写(相对而言)的情境下比较有优势。

B+树的主要优点:

  • 1.结构比较扁平,高度低(一般不超过4层),随机寻道次数少;
  • 2.数据存储密度大,且都位于叶子节点,查询稳定,遍历方便;
  • 3.叶子节点形成有序链表,范围查询转化为顺序读,效率高。相对而言B树必须通过中序遍历才能支持范围查询。

B+树的缺点:

  • 1.如果写入的数据比较离散,那么寻找写入位置时,子节点有很大可能性不会在内存中,最终会产生大量的随机写,性能下降。
  • 2.如果B+树已经运行了很长时间,写入了很多数据,随着叶子节点分裂,其对应的块会不再顺序存储,而变得分散。这时执行范围查询也会变成随机读,效率降低了。

参考:从B+树到LSM树,及LSM树在HBase中的应用

2.LSM树(Log-structured Merge-Tree/日志结构的合并树)#

RocksDB,HBase,Cassandra,Kudu底层使用的是LSM树

LSM树的优点

  • 1.LSM树由于数据按顺序存储,因此可以有效地执行区间查询(从最小值到最大值扫描所有的键),并且由于磁盘是顺序写入的(以顺序方式写入紧凑的SSTable文件,而不必重写树中的多个页),所以LSM-Tree可以支持非常高的写入吞吐量。

LSM树的缺点

  • 1.压缩过程中有时会干扰正在进行的读写操作。即使存储引擎尝试增量地执行压缩,并且不影响并发访问,但由于磁盘的并发资源有限,所以当磁盘执行昂贵的压缩操作时,很容易发生读写请求等待的情况。而B-tree的响应延迟则更具确定性。

参考:LSM树由来、设计思想以及应用到HBase的索引 和 数据密集型应用系统设计第3章

3.跳表#

Redis底层使用的是SkipTable

Mysql的索引为什么使用B+树而不使用跳表?#

B+树是多叉树结构,每个结点都是一个16k的数据页,能存放较多索引信息,所以扇出很高。三层左右就可以存储2kw左右的数据(知道结论就行,想知道原因可以看之前的文章)。也就是说查询一次数据,如果这些数据页都在磁盘里,那么最多需要查询三次磁盘IO。

跳表是链表结构,一条数据一个结点,如果最底层要存放2kw数据,且每次查询都要能达到二分查找的效果,2kw大概在2的24次方左右,所以,跳表大概高度在24层左右。最坏情况下,这24层数据会分散在不同的数据页里,也即是查一次数据会经历24次磁盘IO。

因此存放同样量级的数据,B+树的高度比跳表的要少,如果放在mysql数据库上来说,就是磁盘IO次数更少,因此B+树查询更快。

而针对写操作,B+树需要拆分合并索引数据页,跳表则独立插入,并根据随机函数确定层数,没有旋转和维持平衡的开销,因此跳表的写入性能会比B+树要好。

redis为什么使用跳表而不使用B+树或二叉树呢?#

redis支持多种数据结构,里面有个有序集合,也叫ZSET。内部实现就是跳表。那为什么要用跳表而不用B+树等结构呢?

大家知道,redis 是纯纯的内存数据库。

进行读写数据都是操作内存,跟磁盘没啥关系,因此也不存在磁盘IO了,所以层高就不再是跳表的劣势了。

并且前面也提到B+树是有一系列合并拆分操作的,换成红黑树或者其他AVL树的话也是各种旋转,目的也是为了保持树的平衡。

而跳表插入数据时,只需要随机一下,就知道自己要不要往上加索引,根本不用考虑前后结点的感受,也就少了旋转平衡的开销。

因此,redis选了跳表,而不是B+树。

参考:MySQL的索引为什么使用B+树而不使用跳表?