背景
MySQL中使用的InnoDB存储引擎采用了一种特别的最近最少使用(LRU, Least Recently Used)算法来管理其Buffer Pool中的页(包括数据页和索引页)。Buffer Pool是InnoDB用来缓存数据,以减少磁盘I/O操作的内存区域。正确地管理这个缓存对于数据库的性能至关重要,而LRU算法就是用来决定哪些页应该保留在Buffer Pool中,哪些页应该被替换出去的一种策略。
因为mysql的预读机制, 预读大量无用的数据. 可能会导致缓存失效, 降低性能…
- 刚加载数据的缓存页都是放冷数据区域的头部的.
- 1s过后被访问了才会放热数据区域的头部,热数据区域的缓存页被访问了,就会自动放到头部去。
- 实际上冷数据区域放的都是加载进来的缓存页,最多在1s内被访问过,之后就再也没访问过的冷数据缓存页!
- 而加载进来之后在1s过后还经常被访问的缓存页,都放在了热数据区域里,他们进行了冷热数据的隔离
- 在淘汰缓存的时候,优先淘汰冷数据区域几乎不怎么被访问的缓存页.
冷热数据分离的思想设计LRU链表
设计思路
真正的LRU链表,会被拆分为两个部分,一部分是热数据,一部分是冷数据,这个冷热数据的比例是由innodb_old_blocks_pct参数控制的,他默认是37,也就是说冷数据占比37%。
- 查看配置参数
mysql> SHOW VARIABLES LIKE 'innodb_old_blocks_pct';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_old_blocks_pct | 37 |
+-----------------------+-------+
1 row in set (0.00 sec)
何时数据进入热区
所以MySQL设定了一个规则,他设计了一个innodb_old_blocks_time参数,默认值1000,也就是1000毫秒
mysql>
mysql>
mysql> SHOW VARIABLES LIKE 'innodb_old_blocks_time';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| innodb_old_blocks_time | 1000 |
+------------------------+-------+
1 row in set (0.00 sec)
必须是一个数据页被加载到缓存页之后,在1s之后,你访问这个缓存页,他才会被挪动到热数据区域的链表头部去。
因为假设你加载了一个数据页到缓存去,然后过了1s之后你还访问了这个缓存页,说明你后续很可能会经常要访问它,这个时间限制就是1s,因此只有1s后你访问了这个缓存页,他才会给你把缓存页放到热数据区域的链表头部去。
预读机制以及全表扫描加载进来的一大堆缓存页, 会首先加载到冷数据区域
淘汰数据
直接淘汰冷数据区域的尾部的缓存页,刷入磁盘.
刷入磁盘是一个异步线程操作的.
运行一个定时任务,这个定时任务每隔一段时间就会把LRU链表的冷数据区域的尾部的一些缓存页,刷入磁盘里去,清空这几个缓存页,把他们加入回free链
后台线程同时也会在MySQL不怎么繁忙的时候,找个时间把flush链表中的缓存页都刷入磁盘中,这样被你修改过的数据,迟早都会刷入磁盘的!