浅析存储引擎(3)-B-tree

 

浅析日志结构的存储引擎(1)-bitcask
浅析日志结构的存储引擎(2)-SSTable和LSM-Tree

前面两篇文章介绍了比较好理解的日志结构引擎LSM-Tree,但它们不是最常见的索引类型。目前最广泛使用的索引结构是B-tree。B-tree维护着按key排序的key-value对,这样可以实现高效的key-value查找和区间查询。

一、B-tree的存储

前面的文章提到,日志结构存储引擎将数据库分解为可变大小的段,并且始终按顺序写入段(追加写)。而B-tree将数据库分解成固定大小的块和页,通常是PAGESIZE大小4KB(也可能更大),页是读/写的最小单元,这种设计也符合磁盘的读取规则。

以mysql为例,我们都知道InnoDB的存储数据格式是按主键排序的(MyISAM和InnoDB的区别),数据都存储在最底层叶子节点。某一页被指定为B-Tree的根,如下图:

浅析存储引擎(3)-B-tree_第1张图片

假设我们需要查找key=251,需要沿着200-300的区间,最后找到一个包含单个key=251的父页,再从这个父页中找到存储了key=251的数据页(或者块)的偏移量,从磁盘中读取数据。

假设每层树维护500个叶子节点,那么四级树就可以存储500的4次方*4KB=256TB的数据,查询非常高效。

二、如何解决数据更新的问题?

LSM-Tree的数据更新是追加更新文件(最终删除过时的数据)。

和LSM-Tree不一样,B-Tree-底层的基本写操作是使用新数据覆盖磁盘上的旧页。可以理解为磁头先移动到正确位置,然后旋转盘面,最后用新的数据覆盖相应的扇区(SSD的覆盖写更加复杂,需要擦写更大的数据块,可能会产生写放大)。也就是说当数据被覆盖时,索引上对该页的引用并不需要改变。

当然也有例外情况

情况1:假设新覆盖的数据大小超过了原来的页大小,就需要分裂页,同时覆盖其父页,以更新对两个子页的引用(页的偏移量)。

情况2:假设只增加新key,当找到包含其范围的页时,假设页没有足够的空间来容纳新key,那么就需要分裂为两个半满的页,并且父页也需要更新对子页的引用。如下图,插入新key"334":

浅析存储引擎(3)-B-tree_第2张图片

风险:上述的情况1和情况2在需要更新子页的同时,还需要更新父页对子页的引用,由于至少需要两次磁盘操作,很有可能更新完子页数据库就崩溃了,来不及更新父页,产生了孤儿页。常见B-tree的实现在磁盘上增加重做日志,这是一个仅支持追加修改的文件,作用类似前面讲到的SSTable的日志文件,用于崩溃时恢复数据。

 

参考《数据密集型应用系统设计》

原文出自:https://blog.csdn.net/daiyudong2020/article/details/104717904

end

你可能感兴趣的:(mysql)