|
关键字: buffer pool、控制信息、free链表、flush链表、LRU链表、LRU分段、脏页、chunk
mysql从磁盘上加载数据页到内存中后,会在内存中做一个缓存,下次再需要加载数据页时,先去缓存中查找,如果能找到就直接使用,否则才去磁盘上加载数据页再放入缓存中。这个缓存就是buffer pool。
11.1、系统参数设置
(1)innodb_buffer_pool_size
缓存池是有大小限制的,参数为:innodb_buffer_pool_size,默认是128M。
(2)innodb_old_blocks_pct
LRU链表中,old区域占用的比例。
(3)innodb_buffer_pool_instances
buffer pool实例的个数
(4)innodb_buffer_pool_chunk_size
buffer pool中chunk的大小,默认128M
(5)innodb_old_blocks_time
当old区域中的页被再次访问时,如果再次访问的时间间隔小于innodb_old_blocks_time值的话,那么这个缓存页不会被加入young区域。
11.2、控制信息
对于缓存池buffer pool上的所有缓存页,innodb都用了一个控制信息块来记录这个缓存页的一些相关信息,包括表空间编号、页号、缓存页的地址、各种链表的信息、锁信息、LSN信息。
控制块不占用buffer pool的空间,一个控制块的大小为808字节,控制块和缓存页是一一对应的。
11.3、free链表
所有空闲的缓存页对应的控制块的链表称为free链表。

11.4、flush链表
当内存中的页中的数据被修改了后,并不会立即刷新到磁盘上,而是会在某个时间点去刷新进入磁盘,并且还要更新check_lsn的值。这些被修改了但未刷新回磁盘上的页被称为脏页(dirty page)。脏页形成一个链表,称为flush链表。结构和上图一样。
11.5、LRU链表
LRU:Least Recently Used:最近最少使用
当buffer pool中空闲页用完时,这时候还需要将磁盘中某些数据页加载到内存时,需要淘汰一些最近最少使用的缓存页,然后将新加载的页放入缓存池中。所以Innodb维护了一个LRU链表,处于链表末端的数据就是最近最少使用的数据,可以直接淘汰。
(1)普通的LRU链表
新加载的数据放到链表的头部,再次在缓存中读取的数据重新加入头部。那么头部数据永远是最新最热的数据,链表尾部的数据就是冷数据,可以被淘汰。
(2)划分区域的LRU链表
Innodb将LRU链表划分为了两个部分,分别是young区域和old区域。young区域在链表前端,表示最新最热数据,old区域在链表后端,表示冷数据,可以被淘汰。
young区域和old区域组成了整个LRU链表。其中old区域占用LRU链表的比例可以通过innodb_old_blocks_pct系统变量来控制,默认是37,即old区域占LRU链表的37%,young占用63%。

(3)划分区域的LRU链表的使用方法
当有新的缓存页加入时,先将其加入old链表的头部,如果没有下次访问的话,它是不会被放入young链表的。当在innodb_old_blocks_time间隔后再次访问这个缓存页,说明访问这个缓存页比较频繁,那么会将这个缓存页放入young链表的头部。
当有新的缓存页加入时,如果此时free链表是空的,说明没有空闲的缓存页了,那么就从old链表的尾部开始,淘汰掉old链表尾部的非脏页,如果都是脏页,那么会先将脏页写入磁盘。
(4)划分区域的好处
划分区域可以防止非常多的且使用频率不高的页同时加载到buffer pool中,将真正的热数据给挤到链表末尾被淘汰。导致缓存的命中率不高。
因为innodb预读机制的存在,加载到内存中的数据页不一定会用到,防止这些数据页污染LRU热数据链表。
11.6、脏页的刷新
脏页是在内存中修改过数据的页,需要刷新到磁盘上。innodb有专门的后台线程去刷新buffer pool上的脏页。刷新方式有:
(1)BUF_FLUSH_LRU
后台线程会从LRU链表的尾部扫描一些脏页,然后刷新到磁盘,这种刷新方式称为BUF_FLUSH_LRU
(2)BUF_FLUSH_LIST
脏页有自己的flush链表,后台线程会从这个链表中找到脏页,刷新到磁盘,这种刷新方式称为BUF_FLUSH_LIST
(3)BUF_FLUSH_SINGLE_PAGE
用户线程在从磁盘上读取页时,准备将此页放入buffer pool中。而此时free链表中没有空闲页,且LRU尾部的页全是脏页,那么用户线程会从LRU链表中刷新一个脏页到磁盘中,这种刷新方式称为BUF_FLUSH_SINGLE_PAGE
11.7、多Buffer pool实例和chunk
buffer pool实例可以设置多个,系统参数为innodb_buffer_pool_instances。
每个buffer pool实例中都有多个chunk,设置chunk的目的是为了方便buffer pool的扩容,扩容时,只需扩容chunk大小的整数倍即可。
chunk的大小通过系统参数innodb_buffer_pool_chunk_size指定,默认128M,和innodb_buffer_pool_size默认值一样。也就是说默认buffer pool里只有一个chunk。

11.8、查询buffer pool的状态信息
SHOW ENGINE INNODB STATUS;

(1)Buffer pool size
缓存池的页面数
(2)Free buffers
空闲缓存页面数
(3)Database pages
LRU链表中的页面数
注:因为缓存池中的页还可能分配给自适应哈希索引、Lock信息、Insert Buffer等页,所以Database pages + Free buffers小于Buffer pool size
(4)Old Databases pages
LRU链表中old区域的页面数
(5)Modified db pages
脏页的页面数,也就是flush链表的页面数
(6)Buffer pool hit rate
缓存命中率,是个关键指标,通常不低于95%
information_schema.INNODB_BUFFER_POOL_STATS表中也有buffer pool的相关信息。
11.9、page made young、page made not young
当old区域中的页被再次访问时,如果再次访问的时间间隔小于innodb_old_blocks_time值的话,那么这个缓存页不会被加入young区域,这个操作为page made not young,innodb会记录他的次数。
如果old区域的缓存页被加入到了young区域,这个操作被称为page made young。 |