20、对于LRU链表中尾部的缓存页,是如何淘汰他们刷入磁盘的?
00 分钟
2022-8-26

20、对于LRU链表中尾部的缓存页,是如何淘汰他们刷入磁盘的?

1、Buffer Pool的缓存页以及几个链表的使用回顾

接着我们一起学习下,你的Buffer Pool在运行中被使用的时候,实际上会频繁的从磁盘上加载数据页到他的缓存页里去,然后free链表,flush链表、LRU链表都会在使用的时候同时被使用
比如数据加载到一个缓存页,free链表里会移除这个缓存页,然后LRU链表的冷数据区域的头部会放入这个缓存页。
然后如果你要是修改了一个缓存页,那么flush链表中会记录这个脏页,LRU链表中还可能会把你从冷数据区域移动到热数据区域的头部去。
如果你是查询了一个缓存页,那么此时就会把这个缓存页在LRU链表中移动到热数据区域中也有可能会移动到头部去。
总之,MySQL在执行CRUD的时候,首先就是大量的操作缓存页自己对应的几个链表。
然后在缓存页都满的时候,必然要想办法把一些缓存页给输入磁盘,然后清空这几个缓存页,接着把需要的数据页加载到缓存页里去。
我们已经知道,他是根据LRU链表去淘汰缓存页的,那么他到底是什么时候把LRU链表的冷数据区域中的缓存页刷入磁盘的呢?实际上他是在这几个时间

2、定时把LRU尾部的部分缓存页刷入磁盘

首先第一个时机,并不是在缓存页满的时候,才会挑选LRU冷数据区域尾部的几个缓存页刷入磁盘,而是有一个后台线程,他会运行一个定时任务,这个定时任务每隔一段时间就会把LRU链表的冷数据区域的尾部的一些缓存页,刷入磁盘里去,清空这几个缓存页,把他们加入回free链表。
所以实际上在缓存页没用完的时候,可能就会清空一些缓存页了,如下图所示:
notion image
所以大家会发现,只要有这个后台线程定时运行,可能你的缓存页都没用完呢,人家就给你把一批冷数据的缓存页刷入磁盘,清空出来一批缓存页,那么你就多了一批可以使用的空闲缓存页了
所以如果在一个动态的运行效果中思考,大概就是你不停的加载数据到一些空闲的缓存页里去,然后这些缓存页可能被使用,会在LRU链表中各种移动。然后同时有一个后台线程还不停的把冷数据区域的一些不用的缓存页刷入磁盘中,清空一些缓存页出来。
只要有缓存页被刷入磁盘,大家可以想象一下,那么这个缓存页必然会加入到free链表中,从flush链表中移除,从LRU链表中移除。

3、把flush链表中的一些缓存页定时刷入磁盘

如果仅仅是把LRU链表中的冷数据区域的缓存页刷入磁盘,大家觉得够吗?
明显是不够的,因为在LRU链表的热数据区域里的很多缓存页可能也会被频繁的修改,难道他们永远都不刷入磁盘中了吗?
所以这个后台线程同时也会在MySQL不怎么繁忙的时候,找个时间把flush链表中的缓存页都刷入磁盘中,这样被你修改过的数据,迟早都会刷入磁盘的。
只要flush链表中的一波缓存页被刷入磁盘,那么这些缓存页也会从flush链表和LRU链表中移除,然后加入free链表中去
所以你可以理解为,你一边不停的加载数据到缓存页里去,不停的查询和修改缓存数据,然后free链表中的缓存页不停的在减少,flush链表中的缓存页不停的在增加,LRU链表中的缓存页不停的在增加和移动。
另外一边,你的后台线程不停的在把LRU链表的冷数据区域的缓存区以及flush链表的缓存区,刷入磁盘中来清空缓存页,然后flush链表和LRU链表中的缓存页在减少,free链表中的缓存页在增加
这就是一个动态运行起来的效果

4、实在没有空闲缓存页了怎么办?

那么实在没有空闲缓存页了怎么办呢?
此时可能所有的free链表都被使用了,然后flush链表中有一大堆被修改过的缓存页,LRU链表中有一大堆的缓存页,根据冷热数据进行了分离,大致是如此的效果。
这个时候如果要从磁盘加载数据页到一个空闲缓存页中,此时就会从LRU链表的冷数据区域的尾部找到一个缓存页,他一定是最不经常使用的缓存页。然后把他刷入磁盘和清空,然后把数据页加载到这个腾出来的空闲缓存页去。
这就是MySQL的Buffer Pool缓存机制的一整套运行原理,我们已经完整的讲完了缓存页的加载和使用,以及free链表、flush链表、LRU链表是怎么使用的,包括缓存页是如何刷入磁盘腾出来空闲缓存页的,以及缓存页没有空闲的时候应该怎么处理。
大家首先理解了最近几篇文章之后,就应该完全理解了,MySQL在执行CRUD操作的时候,是如何尽可能基于内存中的缓存来处理的。

5、思考

大家发现没有,如果你在执行CRUD的时候要从磁盘加载数据页到Buffer Pool的缓存页的时候,一旦此时没有空闲的缓存页,就必须从LRU链表的冷数据区域的尾部把一个缓存页刷入磁盘,然后腾出来一个空闲的缓存页,接着你才能基于缓存数据来执行CRUD的操作。
但是如果频繁的出现这样一个情况,那你的很逗CRUD执行的时候,难道都要先刷一个缓存页到磁盘上去?然后再从磁盘上读取一个数据页到空闲的缓存页里来?这样岂不是每次CRUD操作都要执行两次磁盘IO?那么性能岂不是会极差?
所以我们来思考一个问题:你的MySQL的内核参数,应该如何优化,优化哪些地方的行为,才能够尽可能的避免在执行CRUD的时候,经常要先刷一个缓存页到磁盘上去,才能读取一个磁盘上的数据页到空闲缓存页里来?

评论