什么是数据库的“缓存池”?( 二 )


这个时候,他可以用下面的图片来描述:

什么是数据库的“缓存池”?

文章插图
 
当加载数据页到缓存池中的时候,MySQL会从 free 链表中获取一个描述数据的信息,根据描述节点的信息拿到其对应的缓存页,然后将数据页信息放到该缓存页中,同时将链表中的该描述数据的节点移除 。这就是数据页被读取 Buffer Pool 中的缓存页的过程 。
但 MySQL是怎么知道哪些数据页已经被缓存了,哪些没有被缓存呢 。实际上数据库中还有后一个哈希表结构,他的作用是用来存储表空间号 + 数据页号作为数据页的key,缓存页对应的地址作为其value,这样数据在加载的时候就会通过哈希表中的key来确定数据页是否被缓存了 。
什么是数据库的“缓存池”?

文章插图
 
6、Flush链表MySql 在执行增删改的时候会一直将数据以数据页的形式加载到 Buffer Pool 的缓存页中,增删改的操作都是在内存中执行的,然后会有一个后台的线程数将脏数据刷新到磁盘中,但是后台的线程肯定是需要知道应该刷新哪些啊 。
针对这个问题,MySQL设计出了 Flush 链表,他的作用就是记录被修改过的脏数据所在的缓存页对应的描述数据 。如果内存中的数据和数据库和数据库中的数据不一样,那这些数据我们就称之为脏数据,脏数据之所以叫脏数据,本质上就是被缓存到缓存池中的数据被修改了,但是还没有刷新到磁盘中 。
同样的这些已经被修改了的数据所在的缓存页的描述数据会被维护到 Flush 中(其实结构和 free 链表是一样的),所以 Flush 中维护的是一些脏数据数据描述(准确地说是脏数据的所在的缓存页的数据描述)
另外,当某个脏缓存页被刷新到磁盘后,其空间就腾出来了,然后又会跑到 Free 链表中了 。
什么是数据库的“缓存池”?

文章插图
 
7、LRU链表如果系统一直在进行数据库的增删改操作,数据库内部的基本流程就是:
什么是数据库的“缓存池”?

文章插图
 
我们还拿 redis 类做类比,以便更好的帮助大家明白其原理 。Flush 的作用其实类似 redis 的 key 设置的过期时间,所以一般情况下,redis 内存不会不够使用,但是总有特殊的情况,问题往往就是在这种极端和边边角角的情况下产生的 。
如果 redis 的内存不够使用了,是不是自己还有一定的淘汰策略?最基本的准则就是淘汰掉不经常使用到的key 。Buffer Pool 也类似,它也会有内存不够使用的情况,它是通过 LRU 链表来维护的 。LRU 即 Least Recently Uesd(最近最少使用) 。
MySql 会把最近使用最少的缓存页数据刷入到磁盘去,那 MySql 如何判断出 LRU 数据的呢?为此 MySql 专门设计了 LUR 链表,还引入了另一个概念:缓存命中率
# 缓存命中率可以理解为缓存被使用到的频率,举个例子来说:现在有两个缓存页,在100次请求中A缓存页被命中了20次,B缓存页被命中了2次,很显然A缓存页的命中率更高,这也就意味着A在未来还会被使用到的可能性比较大,而B就会被 MySQL 认为基本不会被使用到;说到这里,那LRU究竟是怎么工作的 。假设 MySQL在将数据加载到缓存池的时候,他会将被加载进来的缓存页按照被加载进来的顺序插入到LRU链表的头部(就是链表的头插法),假设 MySQL现在先后分别加载A、B、C数据页到缓存页A、B、C中,然后 LRU 的链表大致是这样子的 。
什么是数据库的“缓存池”?

文章插图
 
现在又来了一个请求,假设查询到的数据是已经被缓存在缓存页B中,这时候 MySQL就会将B缓存页对应的描述信息插入到LRU链表的头部,如下图:
什么是数据库的“缓存池”?

文章插图
 
然后又来了一个请求,数据是已经被缓存在了缓存页C中,然后LRU会变成这样子:
什么是数据库的“缓存池”?

文章插图
 
说到底,每次查询数据的时候如果数据已经在缓存页中,那么就会将该缓存页对应的描述信息放到LRU链表的头部,如果不在缓存页中,就去磁盘中查找,如果查找到了,就将其加载到缓存中,并将该数据对应的缓存页的描述信息插入到LRU链表的头部 。也就是说最近使用的缓存页都会排在前面,而排在后面的说明是不经常被使用到的 。
最后,如果 Buffer Pool 不够使用了,那么 MySQL就会将 LRU 链表中的尾节点刷入到磁盘中,用来给 Buffer Pool 腾出内存空间 。来个整体的流程图给大家看下


推荐阅读