数据库的索引结构B+树、跳表和LSM树
介绍常用于索引的数据结构包括B+树,跳表 和LSM树。还思考了以下问题:
- 为什么Mysql使用B+树做索引而不用B-树或者红黑树
- B树与红黑树的区别
B+树
B+树是一种 B-树的变体,拥有更佳的查询性能。
一个m阶的B-树具有如下几个特征:
- 根结点至少有两个子女
- 每个中间结点都包含k-1个元素和k个孩子,其中m/2 <= k <= m
- 每一个叶子结点都包含k-1个元素,其中 m/2 <= k <= m
- 所有叶子结点都处于同一层
- 每个结点中的元素从小到达排列,结点中k-1个元素正好是k个孩子包含的元素的值域分划
B+树新增的特征:
- 有k个子树的中间结点包含k个元素(B树是k-1个元素),每个元素不保存数据,只保存索引,所有数据都保存在叶子节点
- 所有叶子节点包含了全部元素信息,以及指向这些元素记录的指针,且叶子节点本身依据关键字的大小从小到大顺序链接
- 所欲中间节点元素同时存在于子结点,在子结点元素中是最大或最小元素
重要特性:
-
每一个父结点的元素都出现在子节点中,是子节点的最大(或最小)元素
-
中间节点和叶子节点都带有卫星数据,中间节点仅仅是索引
B+树与B-树相比的优点
优点主要体现在查询性能上,并且配合磁盘的读写特性,B+树中间节点没有卫星数据,所以同样大小的磁盘可以容纳更多的节点元素,也就是数据量相同的情况下,B+树比B-树更加矮胖;另一方面,B+树必须最终查找到叶子节点,而B-树只需要找到匹配元素即可,B-树查询性能不稳定;范围查询上,利用了最后的指针,可以快的多
跳表
跳表是一个特殊的链表,相比一般的链表,有更高的查询效率,可以比拟二叉查找树,平均期望的查找、插入、删除的时间复杂度都是O(log N)
跳表可以视为水平排列(Level) 垂直排列(Tower) 的位置(Position, 对Entry的访问抽象) 的二维集合。
跳表是可以实现二分查找的链表
假设原始链表有N 个元素, 那么一级索引有 N/2个元素,二级索引有N/4个元素,最高级索引有2个元素
2
=
n
/
2
h
;
h
=
l
o
g
2
n
2 = n/2^h; h = log_2n
2=n/2h;h=log2n
每一层最多遍历3个元素,所以查找的复杂度 O(3*log N);空间复杂度O(n)
插入元素之后,需要重建索引;删除索引。。。比较复杂,这里略过
跳表对于范围查询非常有效
参考文章:
- https://www.jianshu.com/p/9d8296562806
LSM树
LSM树 Log-Structred-Tree 是一种存储结构,并不是严格的树装结构。
LSM树的核心特点是利用顺序写来提高写性能,但因为分层(内存和文件两部分设计)的设计会稍微降低读性能。用读性能的稍微减弱,换来高性能写。
- Memtable 是内存中的数据结构,用于保存最近更新的数据,会按照Key有序的组织这些数据。HBase采用跳表来保证内存中的key有序。因为Memtable在内存中,掉电易失,使用WAL(Write-after-log,在redo log也使用了这个技术)技术来保证数据的可靠性。
- Immutable Memtable。当Memtable达到一定大小后,会转化为immutable Memtable。写操作由新MemTable处理,在转存过程中不阻塞数据更新操作
- SStable(Sorted String Table) 有序键值对集合,是LSM树组在磁盘中的数据结构,为了加快SSTabel的读取,可以通过建立key的索引以及布隆过滤器来加快Key的查找。LSM树会将所有数据插入、修改、删除等操作记录保存在内存中,当此类操作达到一定数量,再批量的顺序写入到磁盘中,LSM数据更新是日志式的,不断将Immutabel memtable持久化安东存储即可,不用修改之前的SSTable中的key。因此再不同的SSTable中可能存在相同的key记录,最新的那条记录才是准确的。
- 这样的设计大大提高了写性能,但是也会带来一些问题:
- 冗余存储,需要进行Compact操作来合并多个SSTabel来清除冗余记录
- 读取时,需要从最新的倒着查询,最坏情况需要查询完所有的SSTable,可以通过索引/布隆过滤器来优化查找速度。