简单的LRU链表在Buffer Pool实际运行中,可能导致哪些问题
MySQL的预读机制
这个所谓预读机制,说的就是当你从磁盘上加载一个数据页的时候,他可能会连带着把这个数据页相邻的其他数据页,也加载到缓存里去
预读机制一下子把相邻的数据页加载进缓存,放入LRU链表前面的隐患了,预读机制加载进来的
缓存页可能根本不会有人访问,结果他却放在了LRU链表的前面,此时可能会把LRU尾部的那些被频繁访问的缓存页刷
入磁盘中!
哪些情况下会触发MySQL的预读机制
(1)有一个参数是innodb_read_ahead_threshold,他的默认值是56,意思就是如果顺序的访问了一个区里的多个
数据页,访问的数据页的数量超过了这个阈值,此时就会触发预读机制,把下一个相邻区中的所有数据页都加载到缓存里去
(2)如果Buffer Pool里缓存了一个区里的13个连续的数据页,而且这些数据页都是比较频繁会被访问的,此时就会
直接触发预读机制,把这个区里的其他的数据页都加载到缓存里去
这个机制是通过参数innodb_random_read_ahead来控制的,他默认是OFF,也就是这个规则是关闭的
另外一种可能导致频繁被访问的缓存页被淘汰的场景
全表扫描:把这个表里所有的数据页,都从磁盘加载到Buffer Pool里去
会把之前一直被频繁访问的那些缓存页排到LUR链表的队尾。然后当你要淘汰掉一些缓存页腾出空间的时候,这些高频访问的缓存页反而被淘汰
基于冷热数据分离的思想设计LRU链表
真正MySQL在设计LRU链表的时候,采取的实际上是冷热数据分离的思想
**
真正的LRU链表,会被拆分为两个部分,一部分是热数据,一部分是冷数据,这个冷热数据的比例是由
innodb_old_blocks_pct参数控制的,他默认是37,也就是说冷数据占比37%。
冷数据区域的缓存页什么时候会被放入到热数据区域
MySQL设定了一个规则,他设计了一个innodb_old_blocks_time参数,默认值1000,也就是1000毫秒
也就是说,必须是一个数据页被加载到缓存页之后,在1s之后,你访问这个缓存页,他才会被挪动到热数据区域的链表头部去
这样的设计不会把全表扫描和预读机制的缓存页加到热数据区域链表中,这个是个优雅的设计
缓存页不够用的时候,淘汰缓存页也是从尾部淘汰冷数据区域的缓存页,不会影响热数据区域缓存页,完美的设计
如何将LRU链表的使用性能优化到极致
LRU链表的热数据区域是如何进行优化的
在热数据区域中,频繁的进行移动是不是性能也并不是太好。
热数据区域的访问规则被优化了一下,即你只有在热数据区域的后3/4部分的缓存页被访问了,才会给你移动到链表头部去
如果你是热数据区域的前面1/4的缓存页被访问,他是不会移动到链表头部去的
LRU链表中尾部的缓存页,是如何淘汰他们刷入磁盘的
他到底是什么时候把LRU链表的冷数据区域中的缓存页刷入磁盘的呢?实际上他有几个时机
定时把LRU尾部的部分缓存页刷入磁盘
有一个后台线程,他会运行一个定时任务,这个定时任务:
每隔一段时间就会把LRU链表的冷数据区域的尾部的一些缓存页,刷入磁盘里去,清空这几个缓存页,把他们加入回free链表去
把flush链表中的一些缓存页定时刷入磁盘
lru链表的热数据区域里的很多缓存页可能也会被频繁的修改,也需要刷入磁盘
所以这个后台线程同时也会在MySQL不怎么繁忙的时候,找个时间把flush链表中的缓存页都刷入磁盘中
只要flush链表中的一波缓存页被刷入了磁盘,那么这些缓存页也会从flush链表和lru链表中移除,然后加入到free链表中去
实在没有空闲缓存页了怎么办
此时就会从LRU链表的冷数据区域的尾部找到一个缓存页,他一定是最不经常使用的缓存页!然后把他刷入磁盘和清空,
然后把数据页加载到这个腾出来的空闲缓存页里去
