一、Free链表
通过上一节我们了解了Buffer Pool中的缓存页(16k)与磁盘中的数据页(16K)是一一对应起来的。当我们将数据页加载到Buffer Pool的缓存页中的时候,是如何知道哪些缓存页是空的可以加载数据,哪些缓存页已经有数据了?mysql的解决方案是加入了free链表,free链表是一个双向链表。这个free链表的节点就是上一节我们讲的描述数据块地址,只要缓存页是空闲的,那么缓存页对应的描述数据地址就会被放入到这个free链表中。其次free链表有一个基础节点,他会引用链表的头节点和尾节点,里面还存储了链表中有多少个描述数据块的节点,也就是有多少个空闲的缓存页。
![](https://i-blog.csdnimg.cn/blog_migrate/5caf86190beedc38d3cb0294918f6b55.png)
现在介绍了free链表之后,那么是如何通过free链表将磁盘上的数据页读取到free链表的缓存页的呢?首先,我们需要从free链表里获取一个描述数据块,然后就可以对应的获取到这个描述数据块对应的空闲缓存页,接着我们就可以把磁盘上的数据页读取到对应的缓存页中,同时把相关的一些描述数据写入缓存页的描述数据块里,比如这个数据页所属的表空间之类的信息,最后把那个描述数据块从free链表去除就可以了。
![](https://i-blog.csdnimg.cn/blog_migrate/7a30a66a1713dbe112f340613b92d611.png)
接下来我们发现还有一个问题:哪些数据被读取到缓存页中以免重复读取。数据库有一个哈希表数据结构,其使用表空间+数据页号作为key,缓存页的地址作为value。当你要使用一个数据页的时候,通过“表空间+数据页号”作为key去这个hash表里查一下,如果没有就读取数据页,如果有了,就说明数据页已经读取到缓存页中了。
二、Flush链表
前几节我们说过,如果只是更新了Buffer Pool中的数据,但是没有将Buffer Pool中的数据刷回到磁盘文件中,那么此缓存页是脏数据,脏页。那么有没有想过这些脏页什么时候刷回到磁盘中去呢?为了解决这个问题,数据库这里就引入了另一个和free链表类似的flush链表,这个flush链表本质也是通过缓存页的描述数据块中的俩个指针,让被修改过的缓存页的描述数据块组成一个双向链表。凡是修改过的缓存页都会把他的描述数据块加入到这个flush链表中。
![](https://i-blog.csdnimg.cn/blog_migrate/5fc1dabbb4ead9ff3b8c83fe87274ab7.png)
三、LRU链表
当我们不断的将数据页读取到Buffer Pool的缓存页的时候,由于内存大小的限制迟早会将缓存页用完,当缓存页用完后我们该怎么办呢?所以我们就要淘汰一些没用的缓存页,如果将缓存页刷入磁盘中,腾出来的空闲缓存页,那么应该把哪些缓存页的数据刷入磁盘呢?所以我们就要用到一个LRU链表(LRU:Least Recently Used,最近最少使用)。通过这个这个LRU链表,我们就可以知道哪些缓存页是最近最少使用的,那么当你缓存页需要腾出来一个刷入磁盘的时候,不久可以选择哪个LRU链表中最近最少被使用的缓存页了吗?
LRU链表的工作原理:从磁盘中加载一个数据页到缓存页的时候,就把这个缓存页的描述数据块放到LRU链表头部去,那么只要有数据的缓存页他都会在LRU链表中。并且最近被加载数据的缓存页都会被放到LRU链表的头部。而且缓存页的描述数据块本来在LRU链表的尾部,后续你只要查询或者修改了这个缓存页,那么这个缓存页就会被挪到LRU链表的头部去--最近被访问的缓存页一定在LRU链表的头部。那么在尾部的缓存页就是最近被访问最少的,然后就可以把LRU链表尾部的缓存页刷入磁盘中,释放的缓存页就会加入到free链表中。
下一节我们深入讲解LRU,敬起期待!
![](https://i-blog.csdnimg.cn/blog_migrate/e3b790e7065efb81811046ddd48ba2a8.png)
借道友法力一用:
![](https://i-blog.csdnimg.cn/blog_migrate/166fe080e50a753abdfa871b552d3829.png)
========================== stay hungry stay foolish =============================