十张图“拿捏”MySQL中B+树的生成过程

hello,我是大都督周瑜,这篇文章带你用十张图“拿捏”MySQL中B+树的生成过程。

Hello,我是大都督周瑜,研究过Spring、SpringBoot、Spring Cloud、RabbitMQ、Tomcat、Dubbo、Zookeeper、JVM、Linux、SpringAI、LangChain、MetaGPT、GraphRAG、MySQL…等源码,专注技术的底层源码研究,欢迎关注我的公众号:IT周瑜

当MySQL接收到一条以下SQL时,表示要从t1表中查询数据:

select * from t1 where id = 3;

那表的数据存在哪呢?

自然是磁盘文件,每个表创建的时候都需要专门创建一个文件来存表的一行一行数据,这个文件叫做用户表空间文件,也就是ibd文件。

当执行insert语句时,比如:

insert into t1(id, a, b) values(3, 1, 'zhouyu');

MySQL需要将insert语句解析为一行数据,比如以上insert对应的一行记录为

画板

但是这是逻辑层面的,这一行记录真正要存到磁盘,需要转成二进制。

id和a字段都是int类型,占4个字节,只需要将十进制转成二进制即可,b字段是字符,需要根据设置的字符编码来转成二进制,如果是ascii,那么每个字符对应一个字节。

最终这行记录对应的二进制为

画板

把记录转成二进制之后,就需要存在到文件中了,也就是

画板

如果多存储几行,那就是

画板

这么看其实还是逻辑的,真正在磁盘中行和行之间是没有框框的,而是

画板

因此,当插入了多行数据行后,如果行和行之间没有指定的分割符,那在获取数据的时候就不好区分各行的数据了,因此需要明确定义好行的格式,我们可以在每行记录前定义一个记录头,固定占4个字节,其中一个字节用来存当前记录的长度,也就是字节数,另外三个字节先空着。

画板

如果依次插入的是:

insert into t1(id, a, b) values(3, 1, 'zhouyu');
insert into t1(id, a, b) values(9, 5, 'dadudu');
insert into t1(id, a, b) values(6, 8, 'xiaodudu');

那么对应的就是

画板

不过,不知道大家发现问题没有,现在插入的数据并没有按id升序排序,我们先不管为什么要按id排序,我们先想如何按id进行升序排序,难道移动记录的物理位置吗?

当然不用这么麻烦,我们可以在每行记录再新增一个隐藏字段,叫做next_record,相当于链表节点的指针,这样就可以通过这个next_record来控制记录的逻辑顺序了,而不用移动物理位置。

画板

所谓的记录的指针,就是对应记录在文件中字节的编号,或者叫做偏移量,比如40就是表示文件中第40个字节是当前记录的下一条记录,在InnoDB的设计中是指向了记录的中间,这样既方便获取记录的字段,又方便获取记录的next_record,为了好理解,我们还是把指针箭头画出来

画板

还有一个更严重的问题,假如现在执行where查询,比如:

select * from t1 where b = 'dadudu';

需要遍历每行记录进行判断,那难道每次从文件中获取一行记录进行判断吗?那当然不行,记录多了那得做很多次IO。

因此有了页,每页默认16KB,不过一页能存多少条记录就不是固定的了。

画板

我们假如一页只能存三条,那么现在要存9条记录,就需要3页,也就是3个16KB。

画板

注意,每页中可能有空隙的,所以页和页之间我都留了一点空隙。

innodb每次都会取16kb的数据出来,然后遍历这16kb数据中的每行记录,可是如何知道有没有下一页呢?因此每页除开会存数据行之外,还额外有一个page header的固定空间,用来存page_no和next_page,page_no表示页号,从0开始,next_page表示下一页,存的就是下一页的页号,因为每页固定是16KB,因此只要知道page_no就能从文件中定位到具体页的物理位置,从而读取该页的内容,比如要获取page_no=2的页内容,那就直接从第2*16384个字节开始取,取16384个字节就是page_no=2的页的内容。

画板

那如果数据越来越多呢?有很多页呢?此时执行where查询也需要进行很多次io,性能也不高了。

此时就需要用到索引了,索引的本质就是排序,我们可以额外再开辟16kb,也就是一个新页,page_no=3,该页中记录其他页的页号和最小的id值。

画板

  • (3,0)记录表示指向page_no=0的页,并且该页中最小的id为3
  • (15,1)记录表示指向page_no=1的页,并且该页中最小的id为15
  • (28,2)记录表示指向page_no=2的页,并且该页中最小的id为28

以上其实就是一颗B+树了,只不过如果换成以下这样更容易理解,但真正的实现是上图。

画板

本文旨在描述MySQL中B+树大概的生成过程,忽略了很多细节,比如表空间具体的格式、记录行具体的格式、页的具体格式、B+树的根页转换过程、页的拆分过程、页的重组过程等等,大家如果对这些感兴趣,可以关注我的公众号:IT周瑜。

本文主要内容就分享到这里,希望能得到大家的点赞和分享。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值