本文参考 《MySQL是怎样运行的-从根上理解MySQL》
Buffer Pool是MySQL服务器启动时向操作系统申请的连续的内存空间,默认情况下,Buffer Pool只有128M;在启动时可以在配置文件中通过 innodb_buffer_size 参数设置Buffer Pool的大小,单位为字节
基本元素:控制块和缓冲页
首先Buffer Pool对应的连续内存被分为若干个页面(可以称为缓冲页),这些页面默认大小为16kb;
一个缓冲页拥有一个控制块,控制块存储对应缓冲页的表空间编号、页号、在Buffer Pool中的地址、列表节点等信息;控制块存储在Buffer Pool的前面,缓冲页存储在后面;每个页对应的控制信息大小相同;
碎片:剩余的空间不够分配控制块和缓冲页时,这部分用不到空间就变成了碎片
三个链表
free链表:将buffer pool中空闲的缓冲页对应的控制块放在一个链表中,这个链表就是free链表
flush链表:将buffer pool中被修改过的页面的控制块放在一个链表,这个链表就是flush链表
被修改过的页称为脏页
LRU链表:主要用于解决缓冲区不够的情况,按照一定的规则将存储被访问的缓冲页的控制块
通过创建一个基节点加维护链表的头节点地址、尾节点地址,以及当前节点的数量等信息,该基节点存储在buffer pool外单独申请的空间中
LRU链表淘汰缓冲页策略
主要解决预读和全表扫描这两个问题:
MySQL的预读:
1、线性预读:如果顺序访问某个区的页面超过innodb_read_ahead_threshold参数设定值,就会触发一次异步读取下一个区中的全部页面到buffer pool中的请求
2、随机预读:如果某个区的13个连续的页面且这13个页面在young区域前1/4中都被加载到了buffer pool中,无论这些页面是否是顺序读取的,都会触发一次异步读取本区中所有的其他页面到buffer pool中的请求
全表查询
当对页面再次访问时,默认会将该页面移动到LRU链表的前端,即认为该页可能经常被访问,导致其他高频率的页被淘汰
如何解决?
将LRU链表分成两段,一部分存储访问频率高的缓冲页,称为young区域(热数据);另一部分存储访问频率不高的缓冲页,称为old区域(冷数据);
对页面初次访问加载到LRU的old区域头部,第二次访问放到young头部
解决预读:对于初次加载到buffer pool中的页面,将其移动到old区域的头部,从而不影响young区域的缓冲页
解决全表扫描:如果一个缓冲页第一次访问与后续最后一次访问的时间在某个时间间隔内(默认1s),就不会将该页面从old区域移动到young区域,因为对一个页面的全部访问时间很短
如何判断一个页面是否在buffer pool中?
使用表空间号+页号作为key,缓冲页的地址作为value创建哈希表,通过查询该表判断
刷盘方式
将LRU链表的冷数据刷新一部分到磁盘
从flush链表中刷新一部分到磁盘
配置buffer pool多实例:在启动服务器时指定innodb_buffer_pool_instances的值,值为个数
查看buffer pool状态:SHOW ENGINE INNODB STATUS\G;相关参数看书