数据库大揭秘:10张图告诉你MySQL为什么选B+树做索引?( 三 )


重点关注下这里所说的磁盘页,的大小每个系统可能不一样,就堂主所用的这个centos linux系统来说,通过下面的命令查看磁盘页大小为 4096B 也就是 4KB
[lemon/test]$ getconf PAGE_SIZE4096这些磁盘数据页如果是B+树的叶子节点,那就保存着关键字和数据(就是我们用 INSERT 语句塞进数据库的那些数据) 。

数据库大揭秘:10张图告诉你MySQL为什么选B+树做索引?

文章插图
 
如果是非叶子节点那就保存着关键字和指针(指向子节点的指针),从上图B+树实现的索引示意图也可以看到这两种存储形式的差别 。
数据库大揭秘:10张图告诉你MySQL为什么选B+树做索引?

文章插图
 
内存VS外存当我们在MySQL InnoDB中执行 select 查询语句,这个查找数据的过程是这样的:
  • 沿着B+树索引来查找一个给定关键字(或者范围关键字)的所在的数据行 。
  • 找到数据行所在的磁盘页,把这个磁盘页加载到内存中 。
  • 在内存中进行查找(比如二分查找),最终得到目标数据行内容 。
我们知道磁盘是外部存储设备,那MySQL数据库查找的时候将磁盘中数据读入内存这个过程是非常缓慢的,对于机械硬盘具体来说,从磁盘加载数据需要经过寻道、旋转以及传输的这些过程,时间花费至少是几十毫秒,作为对比,DDR4内存寻址时间仅为6ns左右 。
数据库大揭秘:10张图告诉你MySQL为什么选B+树做索引?

文章插图
 
机械硬盘
内存读取速度是磁盘读取速度的100万倍!虽然现在固态硬盘的速度提升了很多,但和内存比起来还是有很大的差距,所以要尽量减少数据库对磁盘数据页的随机IO操作 。
由于磁盘读写耗时的原因,在高并发系统中关系型数据库MySQL会成为系统性能瓶颈 。
在高并发服务中几十毫秒的的耗时太久了,假设10ms处理一个请求,那1秒处理100个 QPS=100 对比秒杀这一类的场景这个吞吐量完全是不够用的,这也是现在像redis这样的高速缓存数据库会站在前面,为MySQL挡一刀的原因,因为Redis都是内存操作,速度非常快!
数据库大揭秘:10张图告诉你MySQL为什么选B+树做索引?

文章插图
 
Why B+树?为了更方便的说明B树和B+树两种存储结构的差异,我们来对比下两种树实现的索引上读取数据的过程,i再来回答innoDB 引擎为什么选B+树 。
为方便对比,假设我们在B和B+树中我们都要「查询 1 < id < 6 之间」的所有数据行 。
B树索引先来看下如果索引用B树来实现,查找数据的流程图:
数据库大揭秘:10张图告诉你MySQL为什么选B+树做索引?

文章插图
 
在不考虑任何优化的前提下,图中绿色虚线是在B树索引上查找数据的遍历轨迹,来拆解一下:
  1. 从索引树根节点开始,加载磁盘页 page1 找到第一个节点 key=1 数据行(1,小林)不符合 。
  2. 继续通过指针找到磁盘页面page2,加载磁盘页page2到内存,key=2 符合,读取数据行(2, 石头)
  3. 重新加载磁盘页 page1 找到第二个节点 key=3符合,读取数据行(3,轩辕) 。
  4. 继续通过指针加载磁盘页 page4 到内存,key=4 符合,读取数据行(4,小北) 。
  5. 重新加载磁盘页 page1 找到第三个节点 key=5 符合,读取数据行(5,why神) 。
数一数上面的5个步骤有几个「加载磁盘页」字眼,5个,还记得上面我们说的:**加载磁盘数据非常费时!**这种随机大量的磁盘IO造成了B树索引的的性能瓶颈,使其与innoDB索引无缘 。
B+树索引再来看下现在innoDB的用B+树的实现方案吧,同样的查询条件,还是画出数据查找的遍历轨迹:
数据库大揭秘:10张图告诉你MySQL为什么选B+树做索引?

文章插图
 
在不考虑任何优化的前提下,图中绿色虚线是在innoDB B+树索引上查找数据的遍历轨迹,同样来拆解一下:
  1. 从索引树根节点开始,加载磁盘页 page1 找到第一个节点 key=1不符合,继续往下搜索 。
  2. 通过指针找到磁盘页page2, 加载磁盘页page2 到内存,其中存放了key=1、2的数据行,读取符合条件数据行 。
  3. 由于叶子节点间组成双向链表,直接顺着page2 加载磁盘页page3 、 加载磁盘页page4,读取其中符合条件的数据行 。
这其中涉及了4次加载磁盘页的操作,看起来只是比上面的B树少了一次,但这是在我的简单例子,为了便于理解数据量比较少 。


推荐阅读