B Tree 的效率已经很高了,为什么MySQL 还要对B Tree 进行改良,最终使用了B+Tree 呢?
总体上来说,这个B 树的改良版本解决的问题比B Tree 更全面。
我们来看一下InnoDB 里面的B+树的存储结构:
MySQL 中的B+Tree 有几个特点:
1、它的关键字的数量是跟路数相等的;
2、B+Tree 的根节点和枝节点中都不会存储数据,只有叶子节点才存储数据。搜索到关键字不会直接返回,会到最后一层的叶子节点。比如我们搜索id=28,虽然在第一层直接命中了,但是全部的数据在叶子节点上面,所以我还要继续往下搜索,一直到叶子节点。
举个例子:假设一条记录是1K,一个叶子节点(一页)可以存储16 条记录。非叶子节点可以存储多少个指针?
假设索引字段是bigint 类型,长度为8 字节。指针大小在InnoDB 源码中设置为6 字节,这样一共14 字节。非叶子节点(一页)可以存储16384/14=1170 个这样的单元(键值+指针),代表有1170 个指针。
树深度为2 的时候, 有1170^2 个叶子节点, 可以存储的数据为1170*1170*16=21902400。
在查找数据时一次页的查找代表一次IO,也就是说,一张2000 万左右的表,查询数据最多需要访问3 次磁盘。
所以在InnoDB 中B+ 树深度一般为1-3 层,它就能满足千万级的数据存储。
3、B+Tree 的每个叶子节点增加了一个指向相邻叶子节点的指针,它的最后一个数据会指向下一个叶子节点的第一个数据,形成了一个有序链表的结构。
4、它是根据左闭右开的区间[ )来检索数据。
我们来看一下B+Tree 的数据搜寻过程:
1)比如我们要查找28,在根节点就找到了键值,但是因为它不是页子节点,所以会继续往下搜寻,28 是[28,66)的左闭右开的区间的临界值,所以会走中间的子节点,然后继续搜索,它又是[28,34)的左闭右开的区间的临界值,所以会走左边的子节点,最后在叶子节点上找到了需要的数据。
2)第二个,如果是范围查询,比如要查询从22 到60 的数据,当找到22 之后,只需要顺着节点和指针顺序遍历就可以一次性访问到所有的数据节点,这样就极大地提高了区间查询效率(不需要返回上层父节点重复遍历查找)。
总结一下,InnoDB 中的B+Tree 的特点:
1)它是B Tree 的变种,B Tree 能解决的问题,它都能解决。B Tree 解决的两大问题是什么?(每个节点存储更多关键字;路数更多)
2)扫库、扫表能力更强(如果我们要对表进行全表扫描,只需要遍历叶子节点就可以了,不需要遍历整棵B+Tree 拿到所有的数据)
3) B+Tree 的磁盘读写能力相对于B Tree 来说更强(根节点和枝节点不保存数据区,所以一个节点可以保存更多的关键字,一次磁盘加载的关键字更多
4)排序能力更强(因为叶子节点上有下一个数据区的指针,数据形成了链表)
5)效率更加稳定(B+Tree 永远是在叶子节点拿到数据,所以IO 次数是稳定