Mysql中的LRU链表
Posted 小志的博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mysql中的LRU链表相关的知识,希望对你有一定的参考价值。
目录
一、LRU链表的概述
- LRU = Least Recently Used(最近最少使用);
- 由于缓冲区空间有限,如果满了,则需要把旧的移除掉,新的加进来;
- 把使用频繁的数据保留在缓存中,把使用频率低的数据移除;
- 用来记录缓冲页的被使用热度;
- Buffer Pool的缓冲命中率(我们当然是期望命中率越高越好);
假设我们一共访问了n次页,那么被访问的页已经在Buffer Pool中的次数除以n,那么就是Buffer Pool的缓冲命中率。 - 提高命中率的方法——简单的LRU链表。
二、LRU链表的处理逻辑
- 创建LRU链表。
- 当要访问某个页时,如果不在Buffer Pool,则把该页从磁盘加载到缓冲池的缓冲页时,就把该缓冲页对应的控制块作为节点塞到LRU链表的头部。
- 当要访问某个页时,如果在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链表的主要内容,如果未能解决你的问题,请参考以下文章