Mysql内存组件Buffer-Pool分析(2)

272 阅读3分钟

在上一篇中描述了数据页是如何加载进入buffer-pool中的,总的来说查询数据的时候怎么判断缓存页是否存在,怎么利用free链表进行缓存页加载。那么下面有一个问题我们数据加载进入了缓存页,对它进行了修改成为了“脏缓存页”。其实只要缓存页上的数据和我们磁盘文件中的数据页的数据不一致了,那bufferpool缓存页就“脏”了称为脏页。但是这些脏页最终也是要刷到磁盘的。

Flush链表

对于Flush链表而言其结构跟Free链表是一样的,不同的是我们修改了的缓存页都会加入到Flush链表中,后续都要从Flush中获取然后刷新到磁盘。

LRU链表

为什么需要LRU链表,我们思考一个问题 随着我们不断的将数据页加载到缓存页中那么总有一天Free链表会没有空闲的缓存页可以用,那么就会面临一个问题淘汰掉一些缓存页让Free链表有空闲,这样可以用来加载新的数据页到bufferPool中。那么到底淘汰谁呀。 就涉及到一个缓存命中率的概念如果一个缓存页在100次查询中50次都用到了,而另一个缓存自从查询进来就没有使用到,那么我们可以说那一个缓存页命中率太低了。所以引入LRU链表来解决这个问题,所谓LRU就是Least Recently Used,最近最少使用的意思。通过这个LRU链表我们可以知道哪些缓存页是最近最少使用的。

LRU链表的简单原理

现在有了LRU链表我们只需要在查询某个缓存页的时候就把它的描述数据放在LUR链表的头部,也就是说经常被修改和查询的缓存页一定在LUR链表的头部。那当我们没有空闲的缓存页可以使用的时候你就可以从LUR的尾巴找到那个最少被访问的缓存页刷入到磁盘。这样空闲下来缓存页就可以用来加载新的数据。

Mysql的预读机制

所谓预读机制就是当查询一个数据页的时候会把相邻的数据页也查询出来。那什么时候触发预读机制呀?有两点

  1. mysql配置中有个配置innodb_read_ahead_threshold 默认值是56意思是如果顺序访问某个区的数据页超过了这个配置就会把相邻区的全部数据页加载到缓冲区。
  2. 如果缓冲区里加载了相邻的13个数据页,而且这些数据页都频繁的被访问那就会把这个区的全部数据页都加载进来。这个机制是通过innodb_random_read_ahead配置来通知的默认是off的。所以默认情况下生效的是上一条规则。

预读带来的问题

我们上面讲了简单LRU缓存页淘汰机制,但是会有问题!

  1. 我们现在要查询一个数据页但是他触发了预读机制把大量的缓存页加载了进来。这个时候那些通过预读机制进来的缓存页其实并不是经常被访问的。这个时候如果没有空闲缓存页了那从尾部刷入磁盘不合理因为这个尾部的访问频次也很高只是正好被预读机制排挤到了尾部。
  2. 如果我们写了 select * from Users 这样的sql也就是执行全部扫描。那么可能有很大数据量加载到了缓冲区。后续又不使用了。那些进程访问的缓存页就被刷新到磁盘了。