LUR(Least Recently Used)
:最近未使用。
传统的LRU
就是维护了一个链表,链表的头部是最近使用的元素,尾部是最长时间没使用的元素。
mysql
的bufferpool
里有许多的缓存页,从磁盘中的读取的数据就存放在这些缓存页中供mysql
操作,默认的缓存页大小是16Kb
。
mysql
有时会有这样的操作场景,就是对某个大数据量的表进行全表扫描。那么此时会将buffer pool
中的缓存页全部淘汰掉,存进这些全表扫描的数据。那么当下一波热点数据的请求进来的时候,就又需要从磁盘去读取数据到缓存页中,会导致IO
开销增加。
所以MYSQL
对LRU
进行了改造,分为冷/热两部分LRU链表
。当新的数据要拉进缓存但是缓存页的数据满了的时候,就先开始淘汰冷链中的页数据。当冷链中的数据在1秒
内被再次访问的时候,不会放到热链中去,只有在1秒
之后被再次访问了,才会放到热链的头部去。
这个时间由innodb_old_blocks_time
控制,查询语句
show variables like 'innodb_old_blocks_time';
结构图如下所示
什么是预读:当从磁盘上读取一个页的时候,把相邻的其他页也读进来。
这种机制带来的好处:可以节省磁盘io
的时间,直接从内存中读取数据。
触发情况:
顺序访问了磁盘上一个区的多个数据页,当这个数量超过56
(默认的阈值)的时候,innodb
会触发预读机制。这个参数由innodb_read_ahead_threshold控制,查询语句
show variables like 'innodb_read_ahead_threshold';
MySQL
的数据最终是存储在磁盘上的,每次查询数据时,我们先需要把数据加载进缓存,然后读取,如果每次查询的数据都已经存在于缓存了,那么就不用去磁盘读取,避免了一次磁盘 IO
,这是我们最期望的。因此为了尽量在 LRU
链表中缓存更多的缓存页,我们「可以根据服务器的配置,尽量调大 Buffer Pool
的大小。
可以通过如下命令去查看 Buffer Pool 的大小以及 Buffer Pool 实例的个数。
# buffer pool大小
show variables like 'innodb_buffer_pool_size';
# buffer pool实例个数
show variables like 'innodb_buffer_pool_instances';
缓存命中率、缓存页的空闲数、脏页数量、LRU 链表中缓存页个数、冷热数据的比例、磁盘 IO 读取的数据页数量等信息。可以通过如下命令查看:
show engine innodb status;
如果看到 youngs/s
这个值较高,说明数据从冷数据区移到热数据的频率较大,因此可以适当调大热数据所占的比例,也就是减小innodb_old_blocks_pct
参数的值,也可以调大innodb_old_blocks_time
参数的值
如果看到 non-youngs/s
这个值较高,说明数据被加载进缓存当中后,没有被移动到热数据区,这是因为在 1s
内被访问了,这很可能是全表扫描造成的,这个时候就可以去检查一下代码,是不是SQL
语句写得不恰当。