MySQL源码学习(二) Buffer Pool

网友投稿 931 2022-05-29

Mysql源码学习(二) Buffer Pool

Buffer Pool是innodb的核心组件之一,所有数据的读取,都会先放到Buffer Pool中,再读取出来,而不是直接读取datafile文件。那么对于类似笔者这样的新手来说,从哪里开始阅读buffer pool的代码呢?在源代码中,几乎搜不到”buffer pool”这样的关键字?

1.       从buf_pool_t开始

在mysql代码库的src/storage/innobase/include目录下,大家可以找到buf0buf.h头文件,这里就是存放buffer pool核心管理结构的地方。在头文件中, struct buf_pool_t这个定义,就是这个关键结构体。大家可以其中看到一个一个熟悉关键字:”LRU”,没错,大名鼎鼎的LRU链表就在这其中,但还有其他很多的字段,在这里不一一展开,后面会通过与LRU链表最相关的几个链表逐一展开。

2.       6大链表

图1 Buffer Pool 6大链表

如图1所示,buf_pool_t结构体中的6个关键字段: free, LRU, unzip_LRU, flush_list, zip_clean, zip_free分别对应了6大链表。其中:

l  free

它是在系统初始化阶段bufpool进行初始化后唯一显式调用链表初始化函数进行init操作的唯一bufpool链表。系统内的第一个LRU链表块,必然是从free链表中获取到的,当flush模块脏页刷新完成,LRU链表节点就会被清除或者移动到LRU链表结尾等待清除,清除LRU之后的节点仍旧是回归到free链表内。

l  LRU

当有LRU链表为空时,必然从free链表获取空闲节点,并进行异步IO读将页读入bufpool,并加入LRU链表,LRU链表长度过大的情况下,会进行尾部刷新,刷新失败会进行更彻底的直接通过LRU进行脏页刷新(BUF_FLUSH_LRU方式),flush链表节点得到释放脏页完成刷新,并同时把LRU链表的脏块也完成移除

l  Unzip_LRU

本链表实际是LRU链表的一个子集,在压缩页控制块(buf_page_struct)中的压缩页需要进行解压缩以进行各种记录级读写操作时,该链表将发挥作用,因此可以说,插入到了unzip_LRU链表就一定页在LRU链表中,反之则未必。

l  Flush_list

l  zip_clean

本链表以目前的源代码来看仅用于调试功能。

l  zip_free

bufpool6大链表中最特殊的一个,链表的根节点可以看做“是一个指针数组”,伙伴系统的精髓就在于按照2的倍数进行紧邻内存块的合并和拆分,进而达到高效管理、代码复杂度低的效果。这个指针数组按照块大小实际包含4层,1024,2048,4096和8192,每一层基结点只管理同类大小的块。

从图我们可以看出,LRU是整个Buffer Pool的核心,因此我们以LRU为线索展开说明,zip_clean和zip_free较为特殊,本文暂不做详细说明,

3.       LRU分配

从LRU和free的说明可以知道,LRU都是从free链表中获得真正的内存空间的。分配的函数入口就在:storage/innobase/buf目录下的buf0lrn.cc文件中的 buf_LRU_get_free_block函数

MySQL源码学习(二) Buffer Pool

图2 buf_LRU_get_free_block主要流程

图2流程中标志了分配的整个流程,但并不是所有情况下,都会完全执行这个流程,例如:当free链表仍未被使用完时,执行完buf_LRU_get_free_only后就会返回。如果free链表已被用完,或者buf_LRU_get_free_only返回为NULL,则会执行buf_LRU_scan_and_free_block,从已有的LRU列表中找到一个block然后释放。buf_LRU_scan_and_free_block会在第4小节中进行阐述

l  buf_LRU_get_free_only

返回一个空闲的block,这个block来自于free链表,如果free链表为空(内存耗尽),则返回NULL

图3 buf_LRU_get_free_only函数主体

从图3中我们可以看到,首先会从free链表的头部开始便利,找到一个可用的block,并把block的状态设置为BUF_BLOCK_READY_FOR_USE的状态,然后返回。

图4 若无法从free链表中获取到block,则先释放一个LRU钟的block

这里不得不提一下buf_LRU_get_free_block中的这段代码,如果是第一次进行扫描,则会从LRU的尾部开始扫描(LRU的特点,这里可以提升效率),但是如果不是第一次扫描,则会扫描整个LRU。但是如果扫描了一次,还是没找到呢?看下图

图5 强制刷新一个page,并将page放到free链表中

如果扫描超过1次没有找到能释放的LRU,则会让thread sleep一段时间,等待page clean刷一些数据到磁盘上,然后会强制执行一次刷新,将1个page刷到磁盘上,然后再放到free链表中。这里要注意,LRU释放掉一个page后,并不会直接使用这个page,而是将它先放回free链表,然后再按正常流程拿过来。

4.       LRU释放

图6 LRU释放流程

LRU的释放,可以看到LRU的释放,会从unzip_LRU链表和common_LRU链表中进行释放。unzip_LRU我们上面已经介绍,那么common_LRU是什么呢?其实就是LRU本身。另外从这里也可以看出,unzip_LRU就是LRU的一个子集,释放unzip_LRU就是释放LRU本身。

我们再进入buf_LRU_free_from_common_LRU_list函数,我们可以看到:

图7 buf_LRU_free_from_common_LRU_list函数主体

所谓释放LRU,是必须要保证这个page是clean的,如果page不是clean的,就需要刷脏。这也是为什么在分配过程中,有可能出现找不到能释放的page的原因。

更深入的释放流程涉及到buffer pool的内存管理,我们会在下次再进行深入探讨

RDS-MYSQL 云数据库 MySQL RDS MySQL MySQL

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:一个HBase偶现Get慢查询responseTooSlow的分析定位
下一篇:基于Tcl脚本生成Vivado工程及编译
相关文章