Mysql中的LRU链表

Posted 小志的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mysql中的LRU链表相关的知识,希望对你有一定的参考价值。

一、LRU链表的概述

  • LRU = Least Recently Used(最近最少使用);
  • 由于缓冲区空间有限,如果满了,则需要把旧的移除掉,新的加进来;
  • 把使用频繁的数据保留在缓存中,把使用频率低的数据移除;
  • 用来记录缓冲页的被使用热度;
  • Buffer Pool的缓冲命中率(我们当然是期望命中率越高越好);
    假设我们一共访问了n次页,那么被访问的页已经在Buffer Pool中的次数除以n,那么就是Buffer Pool的缓冲命中率
  • 提高命中率的方法——简单的LRU链表。

二、LRU链表的处理逻辑

  1. 创建LRU链表。
  2. 当要访问某个页时,如果不在Buffer Pool,则把该页从磁盘加载到缓冲池的缓冲页时,就把该缓冲页对应的控制块作为节点塞到LRU链表的头部
  3. 当要访问某个页时,如果在Buffer Pool中,则直接把该页对应的控制块移动到LRU链表的头部

三、LRU链表的优缺点

1、优点

  • 所有最近使用的数据都在链表表头,最近未使用的数据都在链表表尾。

2、缺点

  • 由于预读的行为,很多预读的页都会被放到LRU链表的表头。如果这些预读的页都没有用到的话,这样,会导致很多尾部的缓冲页很快就会被淘汰。
  • 如果发生全表扫描(比如:没有建立合适的索引 or 查询时没有where字句),则相当于把原有缓冲页全部都冲刷没了。

四、预读的概述

  • 就是InnoDB认为执行当前的请求时,可能会在后面读取某些页面,于是就预先把这些页面加载到Buffer Pool中。

五、预读的分类(根据触发方式分类)

1、线性预读

  • 如果顺序访问某个区(extent,一个区默认64个页)的页面超过了innodb_read_ahead_threshold(默认56)的值,就会触发一次异步读取下一个区中全部的页到Buffer Pool中的请求。

2、随机预读

  • 默认innodb_random_read_ahead=OFF,如果某个区(extent)有13个连续的页面都已经被加载到了Buffer Pool中,无论这些页面是不是顺序读取的,都会触发一次异步读取本区全部的页到Buffer Pool中的请求。

六、造成Buffer Pool命中率低的原因

  • 加载到Buffer Pool中的页不一定被用到。
  • 如果有非常多的使用频率偏低的页被同时加载到Buffer Pool中,则可能会把那些使用频率非常高的页从Buffer Pool中淘汰掉

七、Buffer Pool命中率低的解决方式

  • InnoDB把LRU链按比例(innodb_old_blocks_pct)分成了两段——young区域old区域

  • LRU链表如下所示:

八、对于简单LRU链表的缺点优化

1、针对预读的优化

  • InnoDB规定,当磁盘上的某个页在初次加载(只是加载,没有涉及读取)到Buffer Pool中的某个缓冲页时,该缓冲页对应的控制块会被放到old区域的头部。这样预读页就只会在old区域,不会影响young区域中使用比较频繁的缓冲页。

2、针对全表扫描的优化

  • 虽然首次加载放到的是old区域的头部,但是由于是全表扫描,会对加载的数据进行访问,那么第一次访问的时候,就会将该页放到young区域的头部。这样仍然会把那些使用频率比较高的页面给“排挤”下去。
  • 由于全表扫描有一个特点,就是它对某个页的频繁访问且总耗时很短。所以,针对这种情况,InnoDB规定,在对某个处于old区域的缓冲页进行第一次访问时,就在它对应的控制块中记录下这个访问时间,如果后续的访问时间与第一次访问的时间在某个时间间隔内(即:innodb_old_blocks_time,默认为1000,单位为ms),那么该页面就不会从old区域移动到young区域的头部,否则将它移动到young区域的头部。

以上是关于Mysql中的LRU链表的主要内容,如果未能解决你的问题,请参考以下文章

何为链表链表示例以及翻转链表

环形链表(哈希表链表)寻找两个正序数组的中位数(数组二分查找)二进制求和(位运算数学)

算法链表链表相关问题总结

LRU 最近最久未使用算法 哈希表+双向链表实现

LRU 最近最久未使用算法 哈希表+双向链表实现

LRU 最近最久未使用算法 哈希表+双向链表实现