MySQL源码学习(二) Buffer Pool

举报
爱咬人的猫 发表于 2018/01/28 20:55:37 2018/01/28
【摘要】 MySQL源码学习(二) Buffer PoolBuffer Pool是innodb的核心组件之一,所有数据的读取,都会先放到Buffer Pool中,再读取出来,而不是直接读取datafile文件。那么对于类似笔者这样的新手来说,从哪里开始阅读buffer pool的代码呢?在源代码中,几乎搜不到”buffer pool”这样的关键字?1. 从buf_pool_t开始在mysql代码

MySQL源码学习() Buffer Pool

Buffer Poolinnodb的核心组件之一,所有数据的读取,都会先放到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大链表

blob.png

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

bufpool最重要的三个链表之二,实际上也是LRU链表的子集,所以读入bufpool的页都通过压缩的或者非压缩的控制块进行管理,最初一定是在LRU链表中,当事务部分(和mini事务部分)完成commit操作时候,实际上就意味着内存写的成功,脏页必然要加入flush链表,并等待异步IO线程(系统主线程子系统组成之一)进行刷新操作(linux原生异步IO和作者Heikki Tuuri自己用条件变量实现的模拟异步IObuf_pool_struct提到过,存储部分会做详细说明)动作。

l  zip_clean

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

l  zip_free

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

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

3.       LRU分配

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

blob.png

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

blob.png

3 buf_LRU_get_free_only函数主体

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

blob.png

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

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

blob.png

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

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

4.       LRU释放

blob.png

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函数,我们可以看到:

blob.png

7 buf_LRU_free_from_common_LRU_list函数主体

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

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


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。