InnoDB存储引擎底层B+树

B+树定义
B+树是为磁盘或其他存取辅助设备设计的一种平衡查找树。在B+树中,所有记录节点都是按照键值的大小顺序存放在同一层的叶子节点上,由各叶子节点指针进行连接。B+树特性:高度、页page、扇出fan out。

举例:InnoDB存储引擎底层B+树
上图B+树中,所有记录节点都在叶子节点上,并且是顺序存放的,如果用户从左边的叶子节点开始顺序遍历,可以得到所有键值的顺序排序:5、10、15、20、25、30、50、55、60、65、75、80、85。

B+树的插入操作
B+树的插入操作,必须保证插入后叶子节点中的记录依然排序。为了保持B+树的平衡,对于新插入的键值,可能需要做大量的拆分页(split)操作。因为B+树结构主要用于磁盘,页的拆分意味着磁盘的操作,所以应该在可能的情况下尽量减少页的拆分功能。

B+树的删除操作
B+树使用填充因子(fill factor)来控制树的删除变化,50%是填充因子可设的最小值。B+树的删除操作同样必须保证删除后叶子节点的记录依然排序。当填充因子小于50%时,这时需要做Index Page的合并操作。

实例:页拆分与页合并
数据库主键使用自增id,与使用普通的无序的id相比,有什么优势吗?有人可能会以身份证号作为唯一主键,那这样会有什么问题呢?

B+树为了维护索引的有序性,每次插入或更新一条记录时,会对索引进行更新。假设之前基于身份证作为索引如下:InnoDB存储引擎底层B+树
现在以身份证号为例(或邮箱地[email protected]),有一个开头是3301的身份证号对应的记录插入数据库中,此时要更新索引,按照顺序,显然3301这个身份证号应该插入到第一个leaf page页的3101与3408之间。因为此时该页已经满了,而index page没有满。此时,需要进行页拆分(page split),操作如下:
(1)拆分第一个leaf page
(2)将中间的节点3408放入到Index page中
(3)小于中间节点3408的记录放左边
(4)大于等于中间节点3408的记录放在右边
操作结果如下:
InnoDB存储引擎底层B+树
这种由于页拆分造成的调整,必然导致性能的下降,尤其以身份证作为主键的话,由于身份正号的随机性,必然造成大量的随机节点的插入,进而造成大量的页分裂,造成性能的急剧下降。
而如果使用自增id作为主键,由于新插入的记录的id是顺序递增的,那么它可能会放到最后那个不满的leaf page中,就不会存在页的拆分问题。