B+树
1、B+树的层级更少:相较于B树B+每个非叶子节点存储的关键字数更多,树的层级更少所以查询数据更快;
2、B+树查询速度更稳定:B+所有关键字数据地址都存在叶子节点上,所以每次查找的次数都相同所以查询速度要比B树更稳定;
3、B+树天然具备排序功能:B+树所有的叶子节点数据构成了一个有序链表,在查询大小区间的数据时候更方便,数据紧密性很高,缓存的命中率也会比B树高。
4、B+树全节点遍历更快:B+树遍历整棵树只需要遍历所有的叶子节点即可,,而不需要像B树一样需要对每一层进行遍历,这有利于数据库做全表扫描。
B+树的优缺点
优点
- 单次请求涉及的磁盘IO次数少(出度d大,且非叶子节点不包含表数据,树的高度小);
- 查询效率稳定(任何关键字的查询必须走从根结点到叶子结点,查询路径长度相同);
- 遍历效率高(从符合条件的某个叶子节点开始遍历即可);
缺点
B+树最大的性能问题在于会产生大量的随机IO
,主要存在以下两种情况:
- 主键不是有序递增的,导致每次插入数据产生大量的数据迁移和空间碎片;
- 即使主键是有序递增的,大量写请求的分布仍是随机的;
LSM树
简介
LSM树(Log Structured Merge Tree),它并不是一颗很严格的树,它只是一种存储结构,目前HBase、LevelDB、RocksDB这些NoSQL存储的都是采用的LSM树
核心组成
MenTable
MenTable是内存中的数据结构,用于保存最近更新的数据,会按照key有序的组织这些数据(如何有序的组织数据并没有明确的定义,例如HBase使用跳跃表来保证内存中key的有序)
Immutable MemTable
当MenTable达到一定大小后,会转化成Immutable Memtable。Immutable MemTable是将MemTable便问SSTable的一种中间状态。写操作由新的MemTable处理,在转存过程中不阻塞数据更新操作。
SSTable(Sorted String Table)
有序键值对集合,是LSM数组在磁盘中的数据结构。为了加快SSTable的读取,可以通过建立key 的索引以及布隆过滤器来加快key的查找。
LSM树会将所有的数据插入、修改、删除等操作记录保存在内存中,当此类操作达到一定数据量后,再批量地顺序写入到磁盘中。
这与B+树不同,B+树数据的更新会直接在元数据所在处修改对应的值,但是LSM树的数据更新是日志式的,当一条数据更新是直接append一条更新记录完成的。这样设计的目的就是为了顺序写,不断的将Immutable MemTable flush到持久化存储即可,而不去修改之前的SSTable中的key,保证了顺序写。
因此当MemTable达到一定大小flush到持久化存储变成SSTable后,在不同的SSTable中,可能存在相同的key的记录,当然最新的那条记录才是准确的,这样设计虽然大大提高了写性能,但同时也会带来一些问题:
- 冗余存储,对于每个key,实际上除了最新那条数据外,其他记录都是荣誉无用的,但是依然占用了存储空间。因此需要进行Compact操作(合并多个SSTable)来清除冗余的记录
- 读取时需要从最新的倒着查询,直到找到某个key的记录。最坏的情况需要查询完所有的SSTable,这里可以通过前面提到的所有/布隆过滤器来优化查找速度
LSM树的Compact策略
Compact(压缩)操作是十分关键的操作,否则SSTable数量会不断膨胀,在Compact策略上,主要介绍两种基本策略:size-tiered和leveled
。
三个概念:
- 读放大:读取数据时实际读取的数据量大于真正的数据量。例如在LSM树中需要现在MemTable查看当前key是否存在,不存在继续从SSTable中寻找
- 写放大:写入数据时实际写入的数据量大于真正的数据量。例如在LSM树中写入时可能触发Compact操作,导致实际写入的数据量远大于该key的数据量
- 空间放大:数据实际占用的磁盘空间比数据真正大小更多。例如冗余存储,对于应该key来说,只有最新的那条记录是有效的,之前的都是可以回收的
size-tiered策略
size-tiered策略保证每层SSTable的大小相近,同时限制每一层SSTable的数量。如上图,每层限制SSTable为N,当每层SSTable达到N后,则触发Compact操作合并这些SSTable,并将合并后的结果写入到下一层成为一个更大的sstable
因此当层数达到一定数量时,最底层的单个SSTable的大小会变得非常大。并且size-tiered策略会导致空间放大比较严重。即使对于同一层的SSTable,每个key的记录是可能存在多份的,只有当该层的SSTable执行compact操作才会消除这些可以的荣誉记录
leveled策略
leveled策略也是采用分层的思想,每一层限制总文件的大小
但是跟size-tiered策略不同的是,leved会将每一层切分成多个大小相近的SSTable。这些SSTable是在该层是全局有序的,意味着每一个key在每一层之多只有一条,不存在冗余记录。
当有一层超过大小限制时,会从中选择至少一个文件和下一层有交集的部分进行合并
,多个不相干的合并是可以并发进行的。
leveled策略相较于size-tiered策略来说,每层内key是不会重复的,这样的话冗余占比较小,因此空间放大问题得到缓解。但是写放大问题会更加突出,compact时将设计level N+1层的全部数据。