MySQL: 11 从磁盘读取数据页到Buffer Pool的时候,free链表的作用

Posted 鮀城小帅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL: 11 从磁盘读取数据页到Buffer Pool的时候,free链表的作用相关的知识,希望对你有一定的参考价值。

1. 数据库启动的时候,初始化Buffer Pool的过程

Buffer Pool 中包含很多个缓存页,同时每个缓存页还有一个描述数据,也可以叫做是控制数据或元数据。

mysql启动的时候,它会按照你设置的Buffer Pool大小,稍微再加大一点,去向操作系统申请一块内存区域,作为 Buffer Pool的内存区域。

然后当内存区域申请完毕之后,数据库就会按照默认的缓存页的16KB的大小以及对应的800个字节左右的描述数据的大小,在Buffer Pool中划分出来一个一个的缓存页和一个一个的对应他们的描述数据。

然后当数据库把Buffer Pool划分完毕之后,其结果如下图所示:

 此时,Buffer Pool中的一个一个的缓存页都是空的,里面什么都没有,要等MySQL运行起来之后,当我们对数据执行增删改查操作的时候,才会把对应的页从磁盘文件里读取出来,放入Buffer Pool 中的缓存页中。

2.基于 free链表 区分缓存页

在从磁盘上读取数据页放入到Buffer Pool中的缓存页的时候,涉及到一个问题,那就是哪些缓存页是空闲的?

在默认情况下磁盘上的数据页和缓存页是一一对应起来的,都是16KB,一个数据页对应一个缓存页。

如何区分哪些缓存页是空闲的?

数据库为Buffer Pool设计了一个 free链表,它是双向链表数据结构,在free链表里,每个节点就是一个空闲的缓存页的描述数据块的地址,也就是说一个缓存页是空闲的,那么它的描述数据块就会被放入到这个 free 链表中。

刚开始启动数据库的时候,可能所有的缓存也都是空的,因为此时可能是一个空的数据库,一条数据都没有,所以此时所有的缓存页的描述数据块,都会被放入到 free 链表中。

 在上图的free链表中,里面的每个节点就是对应缓存页的描述数据块,只要缓存页是空闲的,那么他们对应的描述数据就会加入到这个free链表中,每个节点都会双向链接自己的前后节点,组成一个双向链表。

在free链表中,有一个基础节点,他会引用链表的头结点和尾结点,里面还存储了链表中有多少个描述数据块的节点,也就是有多少个空闲的缓存页。

3.free链表占用的内存空间大小

在free链表中的双向链表是由Buffer Pool中的描述数据块组成的,可以认为每个描述数据块有两个指针,一个是free_pre,另一个是free_next,分别指向自己的上一个free链表的节点,以及下一个free链表的节点。

通过Buffer Pool中的描述数据块的free_pre和free_next两个指针,就可以把所有的描述数据块串成一个free链表。

对于free链表而言,只有一个基础节点是不属于 Buffer Pool的,它是40字节大小的一个节点,里面存放了free链表的头节点的地址,尾节点的地址,还有free链表里当前有多少个节点。

4.如何将磁盘上的页读取到Buffer Pool的缓存页中去

把磁盘上的数据页读取到Buffer Pool中的缓存页里去的原理如下:

首先,从free链表里获取一个描述数据块,然后就可以对应的获取到这个描述数据块对应的空闲缓存页。

 接着就把磁盘上的数据页读取到对应的缓存页里去,同时把相关的一些描述数据写入缓存页的描述数据块里去,比如这个数据页所属的表空间之类的信息,最后把该描述数据块从 free链表里去除就可以了。

 把描述数据块从free链表里移除的方式,假设free链表上有描述语句块01、02、03这三个,当02被使用了之后,需要从free链表中移除,此时就会直接把 01的 free_next 设置为03,把03的free_pre设置为01,这样一来描述数据块02就从 free 链表里失去引用关系了。

 5. 如何知道数据页是否被缓存

阶段小结:我们在执行增删改查操作的时候,是先看看这个数据页有没有被缓存,如果没被缓存就走上面的逻辑,从free链表中找到一个空闲的缓存页,从磁盘上读取数据页写入缓存页,写入描述数据,从 free链表中移除这个描述数据块。

数据库中有一个哈希表数据结构,他会用表空间和+数据页号,作为一个key,然后缓存页的地址作为value。

当你要使用一个数据页的时候,通过 “表空间号+数据页号”作为key去这个哈希表里查一下,如果没有就读取数据页,如果已经有了,就说明数据页已经被缓存了。

也就是说,每次你读取一个数据页到缓存之后,都会在这个哈希表中写入一个key-value对,key就是表空间号+数据页号,value就是缓存页的地址,那么下次如果你再使用这个数据页,就可以从哈希表里直接读取出来它已经放入一个缓存页了。

6.扩展

(1)如何知道数据的数据页号?

需要扫描索引B+树,先从根节点开始扫描。在系统表空间中存储了所有索引对应根节点的页号,知道了页号,直接就可以根据页号*16k去找到磁盘文件对应的数据了;里面用 .slot槽二分法找到对应的那组最大记录(此时是目录项),再往后迭代目录项,知道找到对应的目录项;而这个目录项也会指向对应的页,知道一层一层找到叶子节点。

以上是关于MySQL: 11 从磁盘读取数据页到Buffer Pool的时候,free链表的作用的主要内容,如果未能解决你的问题,请参考以下文章

MySQL: 14 LRU链表在Buffer Pool实际运行中可能导致的问题

MySQL 的 Buffer Pool,终于被我搞懂了

一文了解MySQL的Buffer Pool

Linux buffer/cache异同

MySQL日志顺序读写及数据文件随机读写原理

mysql事务相关的redoLogundoLog