<返回更多

换一个角度解读 Mysql B+tree 索引

2023-04-11  今日头条  做好一个程序猿
加入收藏

InnoDB如何存储数据

MySQL支持多种存储结构,我们常用的就是InnoDB,前面我们在 前面的 " MySQL 的 NULL 值是怎么存放的 " 一文中已经知道记录是按照行来存储的,数据读取并不是以行为单位进行读取的,因为这样效率非常低,InnoDB 是以数据页为单位进行读取的。

也就是说,当用户只需要读取一条记录的时候,并不是将这个记录从磁盘中读出,而是以页为单位,将记录所在的页整体读入内存

InnoDB 数据页默认大小是16KB。

数据页结构分为7个部分,如下图:

 

数据页7个部分作用说明

字段

说明

File header 文件头部

页的一些通用信息

page header 页面头部

页的状态信息

infimum + supremum 最大、最下记录

两个虚拟的伪记录,页中最大、最小记录

User records 用户记录

用户存储行的记录内容

Free space 用户空间

页中还没有被使用的空间

Page Directory 页目录

页中某些记录相对的位置(对记录索引作用)

FileTrailer 文件尾部

校验页是否完整

File header

在 File Header 中有两个指针,分别指向上一个数据页和下一个数据页,连接起来的页相当于一个双向的链表,如下图所示:

 

采用链表的结构是让数据页之间不需要是物理上的连续的,而是逻辑上的连续。

记录在页中的存储

数据页的主要作用是存储记录,也就是数据库的数据,所以重点说一下数据页中的 User Records 是怎么组织数据的。

用户插入数据的时候,记录会按照行格式存储到 User records 部分,在一开始的时候,没有这一部分,每当插入一条记录的时候,都会从Free space 部分划分到 User records 。

当 Free space 这部分的空间完全 被 User records这部分替换之后,也就代表着这个数据页用完了。

过程如下:

 

数据页中的记录按照 主键 顺序组成单向链表,单向链表的特点就是插入、删除非常方便,但是检索效率不高,最差的情况下需要遍历链表上的所有节点才能完成检索。InnoDB是这样的吗? 答案:肯定不是啊!

page Directory(页目录)

我们平时在查找一本书中的某个内容的时候,会怎么做呢?一般会先去看目录,找到对应的页码,然后就可以去对应的页码页看内容了。InnoDB也实现了类似这样一个目录。

目录创建过程如下

  1. 将所有正常的记录(包括 Infimum Supremum 记录,但不包括已经移除到垃圾链表的记录)划分为几个组.
  1. 每个组的最后一条记录(也就是组内最大的那条记录)相当于"带头大哥"组内其余的记录相当于"小弟。带头大哥"记录的头信息中的 owned 属性表示该组内共有几条记录
  1. 将每个组中最后一条记录在页面中的地址偏移量(就是该记录的真实数据与页面中个字节之间的距离〉单独提取出来,按顺序存储到靠近页尾部的地方,这个地方就是 Page Directory 。页目录中的这些地址偏移量称为槽 (Slot) ,每个槽占用 2 个字节,页目录就是由多个槽组成的。

举个例子

如下图5 个槽的编号分别为 0,1,2,3,4,我想查找主键为 11 的用户记录:

看到第三步的时候,可能有的同学会疑问,如果某个槽内的记录很多,然后因为记录都是单向链表串起来的,那这样在槽内查找某个记录的时间复杂度不就是 O(n) 了吗?

这点不用担心,InnoDB 对每个分组中的记录条数都是有规定的,槽内的记录就只有几条:

 

从图可以看到,页目录就是由多个槽组成的,槽相当于分组记录的索引。然后,因为记录是按照「主键值」从小到大排序的,所以我们通过槽查找记录时,可以使用二分法快速定位要查询的记录在哪个槽(哪个记录分组),定位到槽后,再遍历槽内的所有记录,找到对应的记录,无需从最小记录开始遍历整个页中的记录链表。

B+ 树是如何进行查询

上面都是在说一个数据页中的记录检索,因为一个数据页中的记录是有限的,且主键值是有序的,所以通过对所有记录进行分组,然后将组号(槽号)存储到页目录,使其起到索引作用,通过二分查找的方法快速检索到记录在哪个分组,来降低检索的时间复杂度。

当我们需要存储大量的记录时,就需要多个数据页,这时我们就需要考虑如何建立合适的索引,才能方便定位记录所在的页。

为了解决这个问题,InnoDB 采用了 B+ 树作为索引磁盘的 I/O 操作次数对索引的使用效率至关重要,因此在构造索引的时候,我们更倾向于采用“矮胖”的 B+ 树数据结构,这样所需要进行的磁盘 I/O 次数更少,而且 B+ 树 更适合进行关键字的范围查询。

InnoDB 里的 B+ 树中的每个节点都是一个数据页,结构示意图如下:

 

通过上图,我们看出 B+ 树的特点:

我们再看看 B+ 树如何实现快速查找主键为 6 的记录,以上图为例子:

可以看到,在定位记录所在哪一个页时,也是通过二分法快速定位到包含该记录的页。定位到该页后,又会在该页内进行二分法快速定位记录所在的分组(槽号),最后在分组内进行遍历查找。

总结

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>