10解决预读问题的改进的LRU算法

10.1 基于冷热数据分离思想的LRU链表

为了解决上述提到的问题,Mysql在设计LRU链表的时候,采用冷热数据分离的思想。

LRU链表会被拆分成两个部分:一部分是热数据,一部分是冷数据。

冷热数据比率由这个函数控制:

innodb_old_blocks_pct

这个函数默认值是37,也就是说,默认有37%的冷数据。

可以形象的认为,LRU链表的头指针指向热数据区域,LRU链表的尾指针指向冷数据区域。

10.2 实际的加载数据页的过程

数据页第一次被加载进缓存的时候,系统会把数据页存放在冷数据页区的第一个。

innodb_old_blocks_time

默认参数1000

这个参数代表,经过一定时间间隔后,如果这个数据页被访问了,则把这个数据页转移到热数据页区的第一个。

10.3 热数据页区的注意事项

因为热数据页区的缓存页是经常被访问的,所以自然如果经常性的移动这些页,就会造成性能的降低。

所以LRU链表的热数据区域的访问规则有过一定的优化:

只有在热数据区后3/4部分区域的缓存页被访问了,才会移动到LRU表头去。

11 LRU链表末尾刷入磁盘

11.1 定时

在后台有一个线程负责间隔一定时间,把LRU末尾的缓存页写回磁盘,并且加入Free链表之中,从flush链表中删除

11.2 别忘了flush

mysql在不怎么繁忙的时候,找个时间就会把flush链表中的缓存页都写回磁盘之中的。

所以这是一个动态的效果,一边不停地加载数据到缓存页中,不停地查询和修改缓存数据,然后free链表中的缓存页不停地在减少,flush链表中的缓存页不停地在增加。lru链表中的缓存页不停地在增加和移动。
后台线程不停地把lru链表的冷数据区域的缓存页以及flush链表的缓存页写回磁盘之中,然后flush链表和lru链表的缓存页在减少,free链表的缓存页在增加。

12 多线程并发访问BufferPool

12.1 加锁

显然,在多个线程并发的访问BufferPool的时候,必须要加上锁。

所以实际上,很多线程应该是串行的排着队的使用和访问BufferPool。

而且由于BufferPool是在内存之中,而且很多链表的操作都是指针,所以这种性能上的损失可以忽略不计。

但是,在牵涉到IO操作的时候,就会耗时稍微长一点。

12.2 多个BufferPool

一般来说,mysql默认的规则是,如果你给BufferPool分配的内存大小小于1GB,则最多只会给你一个BufferPool。

所以如果我们的内存空间很大, 我们可以设置多个BufferPool

[server]
innodb_buffer_pool_size = 8589934592 // 设置8G大小
innodb_buffer_pool_instance = 4 // 设置4个BufferPool

13 chunk对BufferPool进行动态调整

BufferPool实际上是由很多chunk组成的。每一个chunk的大小可以由下面的代码进行控制

innodb_buffer_pool_chunk_size

默认值128mb

13.1 实际情况

假设8G内存,就可以有4个BufferPool。

然后每个BufferPool由16个chunk组成。

4个BufferPool由4套不同链表(free,flush,lru)

每一个BufferPool中的chunk共享使用同一套链表

13.2 如何动态调整

如果我们现在的BufferPool的大小8GB,我想要增加到16GB,那么我只要重新申请若干个大小为128mb的chunk,然后把这些chunk动态的添加到BufferPool之间就可以了。

14 生产环境之中,如何基于机器配置来合理设置BufferPool

  • BufferPool的大小应该是机器内存的50~60%
  • BufferPool总大小 = (chunk大小 * bufferPool数量)* 系数

查看数据库属性

show engine innodb status
Total memory allocated XXXX;
Dictionary memory allocated XXX;
Buffer Pool Size XXX;
Free Buffer XXX;  说明free链表中有多少个缓存页是可以用的
Database pages XXX; 说明lru链表中有多少个缓存页
Old database pages XXX; 冷数据页区中的数量
Modified db pages XXX;  flush链表中的缓存页数量
Pending reads;   磁盘上等待加载进缓存的数据页数量
Pending writes: LRU 0, flush list 0, single page0   即将从lru链表刷回磁盘的数量,即将从flush中刷回磁盘的数量
Pages made young XXX, not young XXX  已经从lru冷数据区转移到热数据页区的数量,以及在冷数据页区1s内被访问了,还没进入热数据区的数量
xx youngs/s, xx non-youngs/s 
Pages read XXX, create xxx, written xxx  说明已经读取了、创建、写入了多少个缓存页,
xx reads/s, xx creates/s, 1xx writes/s
Buffer Pool hit rate xxx / 1000, young-making rate xxx / 1000 not xxx / 1000 每1000次访问,命中bufferPool多少次
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
Lru len:xxx, unzip_LRU len:xxx   lru链表中缓存页的数量
I/O sum[xxx]:cur[xx], unzip sum[16xx:cur[0]] 50s读取磁盘页的总数,现在正在读取磁盘页的总数