为什么要有B+树?
总的来说B+树也是树的一个分支,属于自平衡树,用来存储数据。数据就离不开“增删改查”操作。
关于自平衡树,我们有AVL树,红黑树等。他们都是基于“数据位于内存”这样的前提下构建的算法。当面临海量数据时,这些算法将不再适用。并且数据量急剧增多,树的高度增加造成查找时间复杂度O(log(h))也会很大。
基于内存的自平衡树存在的问题:
- 1.不适用海量数据
- 2.单个节点存储数据少,导致树的深度过深。查询效率慢。
B+树采用磁盘预读和多叉树的形式解决!
B树的基本概念
- 1.所有的叶子结点都在同一层
- 2.B树定义一个术语:“最小度” t,t的值取决于磁盘块大小page
- 3.根节点至少包含一个key,除根节点之外的节点至少包含t-1 keys
- 4.所有的节点(包括根节点)最多只能包含2t-1个节点
- 5.子节点的数量=当前节点的keys+1
- 6.和其他自平衡树类似。查找,删除和插入的平均时间复杂度为O(logn)
最小度 t为3构建的一颗B树
B+树
B+树是B树的一个变体,也是一种多路搜索树。
- 1.B+树的搜索和B树类似,区别是B+树只有到达叶子节点才命中(B树可以在非叶子节点命中)。
- 2.非叶子节点相当于是叶子节点的索引,所有的关键字都出现在叶子节点的链表中,且链表中的关键字(数据)恰好是有序的;
- 3.更适合做文件索引系统;
B+树与B树对比
B+树的优势:
- 1.采用磁盘预读和单一节点存储更多元素的方式,使得查询的IO次数更少。
- 2.所有的查询最终都会访问到叶子节点,查询性能稳定。
- 3.所有的叶子结点形成有序链表,便于范围查询。
B树的优势:
- 如果经常查询的数据靠近根节点,可以考虑B树;
MySQL buffer pool预读机制
预读机制
InnoDB在I/O的优化上有一个比较重要的特性为预读,预读请求是一个i/o请求,它会异步地在缓冲池中预先回迁多个页面,预计很快就会需要这些页面,这些请求在一个范围内引入所有页面。InnoDB以64个page为一个extent。通过设置读取page的数量来触发预读,每次预读取一个extent。
数据库请求数据的时候,会将读请求交给文件系统,放入请求队列中;相关进程从请求队列中将读请求取出,根据需求到相关数据区(内存、磁盘)读取数据;取出的数据,放入响应队列中,最后数据库就会从响应队列中将数据取走,完成一次数据读操作过程。
接着进程继续处理请求队列,(如果数据库是全表扫描的话,数据读请求将会占满请求队列),判断后面几个数据读请求的数据是否相邻,再根据自身系统IO带宽处理量,进行预读,进行读请求的合并处理,一次性读取多块数据放入响应队列中,再被数据库取走
两种预读算法
InnoDB使用两种预读算法来提高I/O性能:线性预读(linear read-ahead)和随机预读(randomread-ahead)
为了区分这两种预读的方式,我们可以把线性预读放到以extent为单位,而随机预读放到以extent中的page为单位。线性预读着眼于将下一个extent提前读取到buffer pool中,而随机预读着眼于将当前extent中的剩余的page提前读取到buffer pool中。
- 1、线性预读(linear read-ahead)
线性预读方式有一个很重要的变量控制是否将下一个extent预读到buffer pool中,通过使用配置参数innodb_read_ahead_threshold,控制触发innodb执行预读操作的时间。
如果一个extent中的被顺序读取的page超过或者等于该参数变量时,Innodb将会异步的将下一个extent读取到buffer pool中,innodb_read_ahead_threshold可以设置为0-64的任何值(因为一个extent中也就只有64页),默认值为56,值越高,访问模式检查越严格。
mysql> show variables like 'innodb_read_ahead_threshold';
+-----------------------------+-------+
| Variable_name | Value |
+-----------------------------+-------+
| innodb_read_ahead_threshold | 56 |
+-----------------------------+-------+
例如,如果将值设置为48,则InnoDB只有在顺序访问当前extent中的48个pages时才触发线性预读请求,将下一个extent读到内存中。如果值为8,InnoDB触发异步预读,即使程序段中只有8页被顺序访问。
可以在MySQL配置文件中设置此参数的值,或者使用SET GLOBAL需要该SUPER权限的命令动态更改该参数。
在没有该变量之前,当访问到extent的最后一个page的时候,innodb会决定是否将下一个extent放入到buffer pool中。
2、随机预读(randomread-ahead)
随机预读方式则是表示当同一个extent中的一些page在buffer pool中发现时,Innodb会将该extent中的剩余page一并读到buffer pool中。
mysql> show variables like 'innodb_random_read_ahead';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_random_read_ahead | OFF |
+--------------------------+-------+
由于随机预读方式给innodb code带来了一些不必要的复杂性,同时在性能也存在不稳定性,在5.5中已经将这种预读方式废弃,默认是OFF。若要启用此功能,即将配置变量设置innodb_random_read_ahead为ON。
参考链接:
https://www.geeksforgeeks.org/introduction-of-b-tree-2/
https://mp.weixin.qq.com/s/jRZMMONW3QP43dsDKIV9VQ
https://zhuanlan.zhihu.com/p/27700617