数据库的数据结构 LSM-Tree 和 B-tree

数据库最基本的两件事情: 插入和查询

涉及2个问题,如何存储数据和查找数据。

 

最简单的结构

底层结构:一个纯文本文件,其中每行包含一个key-value对

插入: 每次插入即追加新的内容到文件末尾,相同的值不会覆盖。

查询: 查看文件中最后一次出现的值来找到最新的值。

追加到文件尾部的方式通常足够高效,这种机制叫做日志(log),这里的日志表示的是一个仅能追加的记录序列集合。

问题: 查找会很慢,需要遍历从头到尾,o(n)。为了高效的查找,需要新的结构:索引。

索引背后的基本想法都是使用一些额外的空间,来帮助定位到想要的数据。由于每次写数据,需要更新索引,因此任何类型的索引通常都会降低写的速度。

 最简单索引-哈希索引

把每个key和对应的位置的偏移量都建立映射(一般内存中)。通过偏移量,只需要最多一次磁盘寻址,就可以将value加载到内存。 

优化: 如果一直追加到一个文件中,最终会用尽磁盘。将log分解成一定大小的段,然后在这些段上执行压缩合并,丢弃重复的key,只保留最新的。

log结构的优点: 顺序写。追加的形式,方便控制并发和崩溃恢复。合并段可以整理碎片。

哈希索引的缺点: 占内存。区间查询效率低。

 

SSTables和LSM-Tree (log结构)

排序字符串表 SSTable,段文件要按key有序,每个键在每个合并的段文件中只能出现一尺(压缩过程已经确保了)。可以只保存部分key索引,然后找到最近的索引,再继续查找。

插入: 写内存中的tree结构,当大于某个阈值时,写入磁盘。

查询: 先找内存,再找磁盘。

问题: 崩溃会丢失内存中的数据。需要在磁盘上保留单独的日志,每个写入都立即追加到该日志。内存写入磁盘时,该日志可丢弃。

基于合并和压缩排序文件原理的存储引擎通常都被成为LSM(Log-Structured Merge-Tree)存储引擎。

优化: 查询一个不存在的数据,可能很慢。使用布隆过滤器。SSTables压缩和合并的具体顺序和时间,大小分级和分层压缩。

优点: 支持区间查询,顺序写入。

 

B-trees (原地更新)

固定大小的块或页,页是内部读/写的最小单元。这种设计更接近底层的硬件。

插入: 查找到其范围,再添加到改页。可能产生页分裂。

查询: 从tree的根开始查找。

问题: 写操作是使用新数据覆盖磁盘上的旧页。不是顺序写。

对于普通磁盘,需要将磁头首先移动到正确的位置,然后旋转扇面,最后用新数据覆盖相应的扇区。对于SSD,由于SSD必须一次擦除并重写非常大的存储新片块,情况会更为复杂

由于操作可能涉及多个不同的页,可能完成部分页写入之后崩溃。为了从崩溃中恢复,需要额外的数据结构:预写日志(write-ahead log,WAL),也成为重做日志。

 

优化: 

  • 不使用覆盖页来写入和维护WALl来进行崩溃恢复。使用写时复制
  • 保存键的缩略信息,而不是完整的键,将更多的键压入到页中,让树具有更高的分支因子,从而减少层数。(B+树)
  • 相邻子页按顺序保存
  • 双向链表
  • 分形树?

 

对比 B-tree 和 LSM-tree 

  • LSM-tree 写入更快(先写内存,然后顺序写入磁盘)  B-tree读取更快
  • LSM-tree 在压缩过程中有时会干扰正在进行的读写操作。(磁盘资源有效)。对平均时间影响较小,但对较高的百分位数,有时会相当高。而B-tree的响应延迟更具确定性。未合并段越来越多,最后可能造成雪崩。
  • B-tree 的每个键恰好唯一对应索引中的某个位置,可以直接通过建上的锁来实现事务。 log结构可能在不同段有多个副本。

你可能感兴趣的:(数据库的数据结构 LSM-Tree 和 B-tree)