InnoDB 内存管理用的是最近最少使用 (Least Recently Used, LRU) 算法,这个算法的核心就是淘汰最久未使用的数据。
假设按照这个算法,我们要扫描一个 200G 的表,而这个表是一个历史数据表,平时没有业务访问它。那么,按照这个算法扫描的话,就会把当前的 Buffer Pool 里的数据全部淘汰掉,存入扫描过程中访问到的数据页的内容。也就是说 Buffer Pool 里面主要放的是这个历史数据表的数据。
MySQL的LRU实现
InnoDB Buffer Pool 的LRU 算法,是用链表
来实现的。
在 InnoDB 实现上,按照 5:3 的比例把整个 LRU 链表分成了 young 区域
和 old 区域
。
图中 LRU_old
指向的就是 old 区域的第一个位置,是整个链表的 5/8 处。也就是说,靠近链表头部的 5/8 是 young 区域,靠近链表尾部的 3/8 是 old 区域。
- 状态 1,要访问数据页 P3,由于 P3 在 young 区域,因此和优化前的 LRU 算法一样,将其移到链表头部,变成状态 2。
- 之后要访问一个新的不存在于当前链表的数据页,这时候依然是淘汰掉数据页 Pm,但是新插入的数据页 Px,是放在 LRU_old 处。
- 处于 old 区域的数据页,每次被访问的时候都要做下面这个判断:
- 若这个数据页在 LRU 链表中存在的时间超过了 1 秒,就把它移动到链表头部;
- 如果这个数据页在 LRU 链表中存在的时间短于 1 秒,位置保持不变。1 秒这个时间,是由参数
innodb_old_blocks_time
控制的。其默认值是 1000,单位毫秒。