(1)Buffer Pool的缓存页以及几个链表的使用:
Buffer Pool在运行过程中被使用的时候,实际上会频繁的从磁盘上加载数据页到他的缓存页里去。Free链表,flush链表,lru链表都会在使用的时候被同时使用。
比如数据加载到一个缓存页,free链表里会移除这个缓存页,然后lru链表的冷数据区域的头部会放入这个缓存页。
如果你修改了一个缓存页,那么flush链表中会记录这个脏页,lru链表中还可能把这个修改的缓存页从缓存页移动到热数据区域的头部去。 如果你查询了一个缓存页,那么此时就会把这个缓存页在lru链表中移动到热数据区域去,或者在热数据区域中也有可能会移动到头部去。
总之,MySQL在CRUD的时候,首先是大量的操作缓存页以及对应的几个链表。然后在缓存页都满的时候,把一些缓存页刷入磁盘,然后清空这几个缓存页,接着把需要的数据页加载到缓存页里去,实际上是根据优化后的LRU链表去淘汰缓存页。
(2)什么时候把LRU链表的冷数据区域中的缓存页刷入磁盘?
(A) 定时把LRU尾部的部分缓存页刷入磁盘:
首先第一个时机,并不是在缓存页满的时候,才会挑选LRU冷数据区域尾部的几个缓存页刷入磁盘,而是有一个后台线程,运行一个定时任务,这个定时任务每隔一段时间就会把LRU链表的冷数据区域的尾部的一些缓存页刷入磁盘里去,清空这几个缓存页,把他们加入到free链表里去,所以实际上在缓存页没用完的时候,可能就会清空一些缓存页。
所以在一个动态的运行效果中,就是你不停的加载数据页到一些空闲的缓存页里去,然后这些缓存页可能被使用,会在lru链表中各种移动。然后同时有个后台线程还不停的把冷数据区域的一些不用的缓存页刷入磁盘中,清空一些缓存页出来,只要缓存页被刷入磁盘,这个缓存页必须会加入到free 链表中,从flush链表中移除,从lru链表中移除。
(B) 定时把flush链表中的一些缓存页刷入磁盘:
仅仅把LRU链表中冷数据区域的缓存页刷入磁盘明显是不够的,因为在lru链表的热数据区域里有很多缓存页被频繁的修改,难道他们就永远就不刷入磁盘了吗?所以这个后台线程同时也会在MySQL不怎么繁忙的时候,找个时间把flush链表中的缓存页也刷入磁盘中,这样被修改过的数据,迟早都会刷入磁盘的。只要flush链表中的一些缓存页被刷入磁盘,那么这些缓存页也会从flush链表和lru链表中删除,然后加入到free链表中。(如果在LRU链表中有,且在Flush链表中也有,那么在LRU链表中刷入磁盘时还会将对应的这条在Flush链表中也删除)
所以可以想象,不停的加载数据到缓存页里去,不停的查询和修改数据,然后free链表中的缓存页不停的在减少,flush链表中的缓存页不停的增加,lru链表的缓存页不停的在增加和移动;另外一边,后台线程不停的在把lru链表的冷数据区域 和 flush链表的缓存页,刷入磁盘中来清空缓存页,然后flush链表和lru链表的缓存页在减少,free链表的缓存页在不停的增加。这是一个动态运行起来的效果。
(3)实在没有空闲缓存页了怎么办?
此时可能所有的free链表都被使用了,然后flush链表中有一大堆被修改过的缓存页,lru链表中有一大堆的缓存页,根据冷热数据进行了分离,这个时候如果要从磁盘加载数据页到一个空闲的缓存页,此时就会从lru链表的冷数据区域尾部找到一个缓存页,然后把他刷入磁盘和清空,然后把数据页加载到这个腾出来的空闲缓存页里去。(两次IO)
问题:
如果没有空闲的缓存页了,就会从lru链表中刷盘然后加入新来的数据,这样就是刷一个缓存页到磁盘上,然后再从磁盘上读取一个数据页到空闲的缓存页里来,这岂不是要执行两次IO。
所以你的MySQL的内核参数应该如何优化,优化哪些地方的行为才能尽可能的避免在执行CRUD的时候,出现先刷一个缓存页到磁盘上去,才能从磁盘上读取一个数据页到空闲缓存页里?
答:
如何避免上述问题?关键点是你的Buffer Pool有多大,如果你的Buffer Pool的大小扛住了申请缓存页的峰值,这个过程空闲的缓存页在减少,一旦过去了峰值,后台的定时线程会基于flush链表,lru链表释放一些缓存页,然后把释放的缓存页交给free链表,这个过程空闲的缓存页在增加。这个Buffer Pool的过程就不会涉及到buffer poll 满了后在crud时操作两次IO。
扛过MySQL访问的高峰期,后台线程自动刷入缓存页磁盘,那时buffer pool就不会满了。