(五)大白话MySQL的Buffer Pool的free链表、flush链表、LRU链表
(六)大白话MySQL是如何基于冷热数据分离的方案,来优化LRU算法?
(七)大白话MySQL是如何将LRU链表的使用性能优化到极致的?
文章目录
1、LRU链表的热数据区域是如何进行优化的?
接着我们来看看LRU链表的热数据区域的一个性能优化的点,就是说,在热数据区域中,如果你访问了一个缓存页,是不是应该要把他立马移动到热数据区域的链表头部去?
但是你要知道,热数据区域里的缓存页可能是经常被访问的,所以这么频繁的进行移动是不是性能也并不是太好?也没这个必要。
所以说,LRU链表的热数据区域的访问规则被优化了一下,即你只有在热数据区域的后3/4部分的缓存页被访问了,才会给你移动到链表头部去。
如果你是热数据区域的前面1/4的缓存页被访问,他是不会移动到链表头部去的。
举个例子,假设热数据区域的链表里有100个缓存页,那么排在前面的25个缓存页,他即使被访问了,也不会移动到链表头部去的。但是对于排在后面的75个缓存页,他只要被访问,就会移动到链表头部去。
这样的话,他就可以尽可能的减少链表中的节点移动了。
2、定时把LRU尾部的部分缓存页刷入磁盘
我们已经知道,他是根据LRU链表去淘汰缓存页的,那么他到底是什么时候把LRU链表的冷数据区域中的缓存页刷入磁盘的呢?实际上他有几个时机。
首先第一个时机,并不是在缓存页满的时候,才会挑选LRU冷数据区域尾部的几个缓存页刷入磁盘,而是有一个后台线程,他会运行一个定时任务,这个定时任务每隔一段时间就会把LRU链表的冷数据区域的尾部的一些缓存页,刷入磁盘里去,清空这几个缓存页,把他们加入回free链表去!
所以实际上在缓存页没用完的时候,可能就会清空一些缓存页了,我们看下面的图示。
所以大家会发现,只要有这个后台线程定时运行,可能你的缓存页都没用完呢,人家就给你把一批冷数据的缓存页刷入磁盘,清空出来一批缓存页,那么你就多了一批可以使用的空闲缓存页了!
所以如果在一个动态的运行效果中思考,大概就是你不停的加载数据到一些空闲的缓存页里去,然后这些缓存页可能被使用,会在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,导致缓存页被频繁的被使用完,此时会产生什么后果以及如何解决?
如果缓存页被使用完了还得先把一个缓存页刷入磁盘腾出一个空闲缓存页,然后才能从磁盘读取一个自己需要的数据页到缓存页里来。
如果频繁这么搞,那么很多crud操作,每次都要执行两次磁盘IO,一次是缓存页刷入磁盘,一次是数据页从磁盘里读取出来,性能是很不高的。
其实结合我们了解到的buffer pool的运行原理就可以知道,如果要避免上述问题,说白了就是避免缓存页频繁的被使用完毕。那么我们知道实际上你在使用缓存页的过程中,有一个后台线程会定时把LRU链表冷数据区域的一些缓存页刷入磁盘中。
所以本质上缓存页一边会被你使用,一边会被后台线程定时的释放掉一批。
所以如果你的缓存页使用的很快,然后后台线程释放缓存页的速度很慢,那么必然导致你频繁发现缓存页被使用完了。但是缓存页被使用的速度你是没法控制的,因为那是由你的Java系统访问数据库的并发程度来决定的,你高并发访问数据库,缓存页必然使用的很快了!
然后你后台线程定时释放一批缓存页,这个过程也很难去优化,因为你要是释放的过于频繁了,那么后台线程执行磁盘IO过于频繁,也会影响数据库的性能。
所以这里的关键点就在于,你的buffer pool有多大!
如果你的数据库要抗高并发的访问,那么你的机器必然要配置很大的内存空间,起码是32GB以上的,甚至64GB或者128GB。此时你就可以给你的buffer pool设置很大的内存空间,比如20GB,48GB,甚至80GB。
这样的话,你会发现高并发场景下,数据库的buffer pool缓存页频繁的被使用,但是你后台线程也在定时释放一些缓存页,那么综合下来,空闲的缓存页还是会以一定的速率逐步逐步的减少。
因为你的buffer pool内存很大,所以空闲缓存页是很多很多的,即使你的空闲缓存页逐步的减少,也可能需要较长时间才会发现缓存页用完了,此时才会出现一次crud操作执行的时候,先刷缓存页到磁盘,再读取数据页到缓存页来,这种情况是不会出现的太频繁的!
而一旦你的数据库高峰过去,此时缓存页被使用的速率下降了很多很多,然后后台线程会定是基于flush链表和lru链表不停的释放缓存页,那么你的空闲缓存页的数量又会在数据库低峰的时候慢慢的增加了。
所以线上的MySQL在生产环境中,buffer pool的大小、buffer pool的数量,这都是要用心设置和优化的,因为对MySQL的性能和并发能力,都会有较大的影响。
请每位同学,去看看自己负责的系统的buffer pool大小、buffer pool数量、chunk大小,然后看看自己的数据库的机器配置,思考一下,当前设置是否合理?为什么要这样设置?
100、创作不易,更多精品大白话章节,请订阅本专栏,谢谢支持
IT社团出品,必属精品!预计本专栏有100+章节,持续更新中。。。
部分目录如下: