每16kb为一页,连续64个页就是一个区,默认占用1MB,每256个区被划分成一个组。
LRU链表,淘汰算法
LRU(Least Recentiy Used),最近最少使用。
为了防止空闲的缓存页不够用,需要将一些缓存页刷回到磁盘,但是有些缓存页使用很频繁有些就不经常使用,所以需要将最近不经常使用到的缓存页刷回到磁盘,LRU链表就是存放最近很少使用的缓存页的。
当把数据页加载到缓存页的时候就把这个缓存页的描述数据块放到LRU链表的头部,所有刚被加载到缓存页的描述数据块都被放到头部,而且后续如果查询或者修改了某一个缓存页里的数据也将其放到LRU链表的头部。这样的话LRU链表的尾部一定就是最少被使用的缓存页了。
MySQL预读机制
线性预读(linear read-ahead)。参数innodb_read_ahead_threshold,默认值是56,意思是当加载数据页时,顺序的访问了一个区里的多个数据页,如果访问的数据页的数量超过了这个阈值就会触发预读机制,会把相邻的区中的所有数据页都加载到缓存中。
随机预读(randomread-ahead)。innodb_random_read_ahead参数,默认时OFF,也就是关闭的。当这个规则打开时,如果buffer pool里缓存了一个区中连续的13个数据页而且这些都是被经常访问的话,就会将这个区其他的数据页都加载到缓存中去。在5.5中已经将这种预读方式废弃,默认是OFF。若要启用此功能,即将配置变量设置innodb_random_read_ahead为ON。
这时,通过预读机制加载到buffer pool的数据如果都放到LRU链表的头部会导致其他被经常访问的数据移动到尾部从而被淘汰刷回到磁盘,这是很不合理的,因为通过预读机制加载的数据可能不会用到。除了通过预读机制导致频繁被访问的缓存页被淘汰之外全表扫描也会导致这个情况,比如select * from xxx,不加where条件。
冷热数据分离的LRU
实际上LRU链表会被分为两个部分,热数据部分(young区域)和冷数据部分(old区域)。每个部分所占的比例可以通过innodb_old_blocks_pct参数来指定,默认为37,意思是冷数据占比37%。实际上数据第一次被加载到缓存的时候是放在冷数据区链表的头部。当被加载到冷数据区的头部之后经过指定的时间后被访问了的数据就会被移动到热数据区的链表头部。这个指定时间是通过innodb_old_blocks_time指定的,默认为1000ms也就是1秒。只有当被访问的缓冲页位于热数据区(young区域)后面的3/4的数据时,才会被移动到LRU链表的头部。(降低调整LRU链表的频率,从而提升性能
刷盘时机
mysql后台线程会定时的把LRU链表冷数据区尾部的一些缓存页刷回到磁盘,并将其从LRU链表去除加入到free链表。除此之外这个后台线程还会在MySQL不怎么繁忙的时候将flush链表中存放的脏数据刷回到磁盘并将其从LRU链表中去除加回到free链表。当实在没有空闲的缓存页的时候就会将LRU链表冷数据区尾部的缓存页刷回到磁盘,空出缓存页,再把用到的数据从磁盘加载到缓存,但是这就有了两次磁盘IO,如果很频繁的这样操作会很影响性能。
缓存命中率越高说明基于缓存的操作就越多,性功越高。show engine innodb status命令可以查看当前innodb里的情况