ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
### Buffer Pool 为了缓存磁盘中的页,在`MySQL`服务器启动的时候就向操作系统申请了一片连续的内存,他们给这片内存起了个名,叫做`Buffer Pool`(中文名是`缓冲池`)。 默认情况下`Buffer Pool`只有`128M`大小。当然如果你嫌弃这个`128M`太大或者太小,可以在启动服务器的时候配置`innodb_buffer_pool_size`参数的值,它表示`Buffer Pool`的大小,就像这样: ~~~ [server] # 单位是字节 innodb_buffer_pool_size = 268435456 ~~~ ### Buffer Pool内部组成 `Buffer Pool`中默认的缓存页大小和在磁盘上默认的页大小是一样的,都是`16KB`。 为了更好的管理这些在`Buffer Pool`中的缓存页,每一个缓存页都创建了一些所谓的`控制信息`,这些控制信息包括该页所属的表空间编号、页号、缓存页在`Buffer Pool`中的地址、链表节点信息、一些锁信息以及`LSN`信息 每个缓存页对应的控制信息占用的内存大小是相同的,我们就把每个页对应的控制信息占用的一块内存称为一个`控制块`吧,**控制块和缓存页是一一对应的**,它们都被存放到 Buffer Pool 中,其中控制块被存放到 Buffer Pool 的前边,缓存页被存放到 Buffer Pool 后边,所以整个`Buffer Pool`对应的内存空间看起来就是这样的: ![](https://img.kancloud.cn/02/68/0268de28d90626f69a8a7eca13f8e7cc_1004x339.png) 每个控制块大约占用缓存页大小的5%,在MySQL5.7.21这个版本中,每个控制块占用的大小是808字节。而我们设置的innodb_buffer_pool_size并不包含这部分控制块占用的内存空间大小,也就是说InnoDB在为Buffer Pool向操作系统申请连续的内存空间时,这片连续的内存空间一般会比innodb_buffer_pool_size的值大5%左右。 ### free链表的管理(空闲链表) 启动`MySQL`服务器的时候,需要对`Buffer Pool`初始化,就是先向操作系统申请`Buffer Pool`的内存空间,然后把它划分成若干对控制块和缓存页。此时`Buffer Pool`是空的。随着程序的运行,会不断的有磁盘上的页被缓存到`Buffer Pool`中。但是这时候没法判断`Buffer Pool`中那些缓存页是使用的,那些是空闲的。这个时候缓存页对应的`控制块`就派上大用场了,我们可以把所有空闲的缓存页对应的控制块作为一个节点放到一个链表中,这个链表也可以被称作`free链表`(或者说空闲链表)。 刚刚完成初始化的`Buffer Pool`中所有的缓存页都是空闲的,所以每一个缓存页对应的控制块都会被加入到`free链表`中, ![](https://img.kancloud.cn/28/0b/280b6775ce9f9a84d07a7b36de16679a_1010x606.png) 从图中可以看出,我们为了管理好这个`free链表`,特意为这个链表定义了一个`基节点`,里边儿包含着链表的头节点地址,尾节点地址,以及当前链表中节点的数量等信息。这里需要注意的是,链表的基节点占用的内存空间并不包含在为`Buffer Pool`申请的一大片连续内存空间之内,而是单独申请的一块内存空间。 链表基节点占用的内存空间并不大,在MySQL5.7.21这个版本里,每个基节点只占用40字节大小。后边我们即将介绍许多不同的链表,它们的基节点和free链表的基节点的内存分配方式是一样一样的,都是单独申请的一块40字节大小的内存空间,并不包含在为Buffer Pool申请的一大片连续内存空间之内。 有了这个`free链表`之后事儿就好办了,每当需要从磁盘中加载一个页到`Buffer Pool`中时,就从`free链表`中取一个空闲的缓存页,并且把该缓存页对应的`控制块`的信息填上(就是该页所在的表空间、页号之类的信息),然后把该缓存页对应的`free链表`节点从链表中移除,表示该缓存页已经被使用了~ ### 缓存页的哈希处理 我们怎么知道该页在不在`Buffer Pool`中呢?难不成需要依次遍历`Buffer Pool`中各个缓存页么?一个`Buffer Pool`中的缓存页这么多都遍历完岂不是要累死? 根据`表空间号 + 页号`来定位一个页的,也就相当于`表空间号 + 页号`是一个`key`,`缓存页`作为`value`创建一个哈希表,在需要访问某个页的数据时,先从哈希表中根据`表空间号 + 页号`看看有没有对应的缓存页,如果有,直接使用该缓存页就好,如果没有,那就从`free链表`中选一个空闲的缓存页,然后把磁盘中对应的页加载到该缓存页的位置。 ### flush链表的管理 如果我们修改了`Buffer Pool`中某个缓存页的数据,那它就和磁盘上的页不一致了,这样的缓存页也被称为`脏页`(英文名:`dirty page`)。 由于频繁的往磁盘中写数据会严重的影响程序的性能,所以每次修改缓存页后,我们并不着急立即把修改同步到磁盘上,而是在未来的某个时间点进行同步。 由于不立即同步到磁盘,mysql把凡是修改过的缓存页对应的控制块都会作为一个节点加入到一个链表中,因为这个链表节点对应的缓存页都是需要被刷新到磁盘上的,所以也叫`flush链表`。链表的构造和`free链表`差不多,假设某个时间点`Buffer Pool`中的脏页数量为`n`,那么对应的`flush链表`就长这样: ![](https://img.kancloud.cn/44/fb/44fb0604dad42a0728b29a18a0bb03e6_1010x591.png)