基于冷热数据分离的思想设计LRU链表
为了解决上一讲我们说的简单的LRU链表的问题,真正MySQL在设计LRU链表的时候,采取的实际上是冷热数据分离的思想。
之前一系列的问题,就是因为所有缓存页都混在一个LRU链表里,才导致的。
所以真正的LRU链表,会被拆分为两个部分,一部分是热数据,一部分是冷数据,这个冷热数据的比例是由innodb_old_blocks_pct参数控制的,他默认是37,也就是说冷数据占比37%。
这个时候,LRU链表实际上看起来是下面这样子的。
冷热分离的LRU加载数据的规则
首先数据页第一次被加载到缓存的时候,实际上这个时候,缓存页会被放在LRU冷数据区域的链表头部,我们看下面的图,也就是第一次把一个数据页加载到缓存页之后,
这个缓存页实际上是被放在下图箭头的位置,也就是冷数据区域的链表头部位置。
冷数据如何变成热数据?MySQL设定了一个规则,他设计了一个innodb_old_blocks_time参数,默认值1000,也就是1000毫秒,也就是说,必须是一个数据页被加载到缓存页之后,在1s之后,你访问这个缓存页,他才会被挪动到热数据区域的链表头部去。
因为假设你加载了一个数据页到缓存去,然后过了1s之后你还访问了这个缓存页,说明你后续很可能会经常要访问它,这个时间限制就是1s,因此只有1s后你访问了这个缓存页,他才会给你把缓存页放到热数据区域的链表头部去。
冷热分离如何解决预加载机制产生的问题
当LRU区分冷热数据之后,如果有预读机制加载的大量缓存页,这里则会首先放在冷数据前面
假设此时热数据区域也有加载的缓存页,那么热数据应在链表的前部分,如果被频繁访问,仍然会被移动到热数据的头部的。
而预读机制和全表扫描加载进来的一大堆缓存页,此时都在冷数据区域里,跟热数据区域里的频繁访问的缓存页,是没关系的!
预读机制和全表扫描机制加载进来的缓存页,什么时候能进热数据区域呢?
如果仅仅是一个全表扫描的查询,此时你肯定是在1s内就把一大堆缓存页加载进来,然后就访问了这些缓存页一下,通常这些操作1s内就结束了。
所以基于目前的一个机制,可以确定的是,这种情况下,那些缓存页是不会从冷数据区域转移到热数据区域的!
除非在冷数据区域里的缓存页,在1s之后还被人访问了,那么此时他们就会判定为未来可能会被频繁访问的缓存页,然后移动到热数据区域的链表头部去!
如果此时缓存页不够用,会怎么处理
现在就很方便了,直接找到LRU链表中的冷数据区域的尾部的缓存页,这里肯定是之前被加载进来的,而且加载进来1s过后都没人访问过,说明这个缓存页压根儿就没人去访问,就是冷数据!
所以此时就直接淘汰冷数据区域的尾部的缓存页,刷入磁盘,就可以了。
总结
通过这几篇文章的学习,理解了LRU链表的设计机制,刚加载数据的缓存页都是放冷数据区域的头部的,1s过后被访问了才会放热数据区域的头部,热数据区域的缓存页被访问了,就会自动放到头部去。
这样的话,实际上冷数据区域放的都是加载进来的缓存页,最多在1s内被访问过,之后就再也没访问过的冷数据缓存页!
而加载进来之后在1s过后还经常被访问的缓存页,都放在了热数据区域里,他们进行了冷热数据的隔离。