今天这篇就和大家讲解一下Buffer Pool里的free链表、flush链表、LRU链表以及mysql是如何基于冷热数据分离的方案,来优化LRU算法淘汰机制的
1.free链表是什么?有什么作用?
在了解free链表之前我们先思考一个问题:我们都知道buffer pool会将自己的内存空间划分成多个16kb大小的数据页用来缓存数据,当读取磁盘文件的时候,会将磁盘文件里的数据写入buffer pool。那么问题来了,磁盘文件写入缓存数据到buffer pool中的数据页时,是怎么区分哪些缓存页是空闲的,哪些是有数据的?总不能一个个的去判断吧?
这时候就需要我们的free链表出场了,没错,free链表的作用就是用来记录哪些缓存页是空闲状态的。
free链表本身是一个双向链表的数据结构,链表中每个节点就是一个空闲缓存页的存放地址,也就是说buffer pool中的所有空闲缓存页,都会被记录到free链表中。此外还会有一个基础节点,基础节点记录了free链表的节点数量以及头结点和尾节点的指针。
另外还有一点需要注意,这个free链表里的节点其实就是buffer pool里的描述数据块,只不过是在描述数据块里增加了两个指针,分别用来记录free链表的上下节点,因此不会出现free链表节点和描述数据块冲突的情况。为了方便大家理解,我花了一个结构图,如下:
当从磁盘读取完数据,此时会先从free链表获取一个空闲缓存页节点,将相关的一些数据写入缓存页的描述数据块中(数据页所属表空间,数据页编号等信息),然后将数据写入到缓存页,之后再将该节点从free链表中去除。
2.flush链表是什么?有什么作用?
同样,在了解flush链表之前,我们还是来思考一个问题:我们都知道buffer pool里的缓存数据有增删改查的磁盘数据,但是写入磁盘的时候,肯定只会写入修改过的数据到磁盘,而查询操作的数据是不需要写入磁盘的,因为根本没有意义,那么问题来了,buffer pool是怎么区分那些数据是需要写入到磁盘中的呢?
有请我们的flush链表闪亮登场!!!没错,这就是flush链表在buffer pool中的作用。
flush链表的数据结构和free链表几乎是一样的,也是双向链表结构,也是有个基础节点,记录了头尾节点和链表节点数量。他同样也是描述数据块,只不过是在描述数据块中增加了两个指针,分别记录了flush链表的上下节点。
凡是被修改过的缓存页,都会把描述数据块增加到flush链表中,后台会有IO线程去获取flush链表的节点,然后将数据写入到磁盘中,并在flush链表中移除。数据结构如下:
3.LRU算法淘汰机制
由前面我们可以得知,磁盘写入数据到缓存时,会先从free链表获取一个空闲缓存页,然后再将数据页写入缓存页,如我我们有大量的crud操作,大量获取空闲节点,是否会出现没有空闲节点的情况,答案肯定是会的。
那么出现这种情况需要怎么处理呢?这时buffer pool只能淘汰一些缓存页来供新的数据页使用,那么该淘汰哪些缓存页呢?假如我们现在有两个缓存页 ,我们的10个请求中有8个都会用到1缓存页,如果此时让我们淘汰一个,你会淘汰谁?脑子没问题的肯定是会淘汰缓存2啊,因为1缓存用到多啊,这就叫LRU算法淘汰机制。
4. LRU链表
回到刚才的问题,free链表里的空闲节点没有了,此时buffer pool需要淘汰一些缓存页来存储新的数据页,那么他就会根据LRU算法淘汰机制去淘汰一些缓存页,那么问题来了,buffer pool是怎么知道哪些缓存页是经常用到的,哪些是不经常用的?
有请LRU链表闪亮登场!!!没错,LRU链表的作用就是用来标识哪些缓存页常用,那些不常用。
LRU链表也是双向链表结构,也是有个基础节点,记录了头尾节点和链表节点数量。他同样也是描述数据块,只不过是在描述数据块中增加了两个指针,分别记录了LRU链表的上下节点。他也是描述数据块,在描述数据块里加了两个LRU链表的指针。
只不过LRU链表分两部分,一部分是热数据,另一部分是冷数据。大体结构如下:
Buffer Pool默认LRU链表里的冷数据是37%,也就是LRU链表的冷数据占总长的37%,并且是在链表后端,前端是热数据。
那么冷热数据是如何保存的呢?
首先数据页第一次加载到缓存的时候,缓存页会被放到冷数据的链表表头。当这个数据1s之后再次被访问,说明这个数据可能会被频繁访问,这个缓存页就会被保存到热数据的链表里,并且在冷数据里去除。这里需要注意,因为热数据链表里的数据是经常访问的数据,所以不适合频繁移动数据到表头,这样对性能不好,所以mysql这里做了一个优化,就是如果你是热数据前面的1/4缓存页被访问,他是不会被移动到链表表头的,只有后面3/4的数据被访问,才会将数据移动到热数据链表表头。这样可以尽可能的减少节点的移动次数。
5.关于入盘的知识点:
1.mysql会有后台线程,运行一个定时任务,每隔一段时间都会将LRU链表里的冷数据尾端的一些缓存写入到磁盘,并清空这几个缓存页,写入到free链表。
2.后台线程同时会在mysql不怎么繁忙的时候,把flush链表里的缓存数据写入到磁盘。然后这些缓存页就会在flush链表和LRU链表中移除,然后加到free链表中。
整体流程:
mysql一边不停的加载数据到缓存,不停的查询和修改缓存数据,free链表的缓存页不停的减少,flush链表的缓存页不停的增加,LRU链表的数据不停的在移动。另一边,后台线程在不停的把LRU链表的冷数据尾部缓存页和flush链表的缓存页写入磁盘,然后LRU链表和flush链表的缓存页在不断减少,free链表的缓存页在不断增加。
6.如果实在没有空闲缓存了怎么办?
可能会出现free链表都被使用了,flush链表有一大堆被修改的缓存页,LRU链表有一大堆的缓存,此时如果来了一个新的数据需要加入缓存中,mysql会怎么处理?
mysql这时就会从LRU冷数据链表中的尾端找到一个缓存页,这个缓存页一定是最不常用的缓存页,然后把它写入磁盘,清空缓存页,再把最新的缓存写到这个缓存页中。