答案是 LRU 算法
LRU是**(L**east Recently Used)的简称,表示最近最少使用,Redis 的内存淘汰策略中也有用到 LRU。
但是 InnoDB 所采用的 LRU 算法和传统的 LRU 算法还不太一样,InnoDB 使用的是改良版的 LRU。那为啥要改良?这就需要了解原生 LRU 在 MySQL 有啥问题了。
在实际的业务场景下,很有可能会出现全表扫描的情况,如果数据量较大,那么很有可能会将之前 Buffer Pool 中缓存的热点数据全部换出。这样一来,热点数据被再次访问时,就需要执行 I/O 操作,而这样就会导致该段时间 MySQL 性能断崖式下跌。而这种情况还有个专门的名词,叫——缓冲池污染。
这也是为什么 InnoDB 要对 LRU 算法做优化。
优化之后的链表被分成了两个部分,分别是 New Sublist 和 Old Sublist,其分别占用了 Buffer Pool 的 3/4 和 1/4。
链表的前 3/4,也就是 New Sublist 存放的是访问较为频繁的页。而后 1/4 也就是 Old Sublist 则是反问的不那么频繁的页。Old Sublist中的数据,会在后续 Buffer Pool 剩余空间不足、或者有新的页加入时被移除掉。
了解了链表的整体构造和组成之后,我们就以新页被加入到链表为起点,把整体流程走一遍。首先,一个新页被放入到Buffer Pool之后,会被插入到链表中 New Sublist 和 Old Sublist 相交的位置,该位置叫MidPoint。
该链表存储的数据来源有两部分,分别是:
MySQL 的预读线程预先加载的数据
用户的操作,例如 Query 查询
默认情况下,由用户操作影响而进入到 Buffer Pool 中的数据,会被立即放到链表的最前端,也就是 New Sublist 的 Head 部分。但如果是 MySQL 启动时预加载的数据,则会放入MidPoint中,如果这部分数据再次被用户访问过之后,才会放到链表的最前端。
这样一来,虽然这些页数据在链表中了,但是由于没有被访问过,就会被移动到后 1/4 的 Old Sublist中去,直到被清理掉。
Log BufferLog Buffer 用来存储那些即将被刷入到磁盘文件中的日志,例如 Redo Log,该区域也是 InnoDB内存的重要组成部分。Log Buffer 的默认值为16M,如果我们需要进行调整的话,可以通过配置参数innodb_log_buffer_size来进行调整。
当 Log Buffer 如果较大,就可以存储更多的 Redo Log,这样一来在事务提交之前我们就不需要将 Redo Log 刷入磁盘,只需要丢到 Log Buffer 中去即可。因此较大的 Log Buffer 就可以更好的支持较大的事务运行;同理,如果有事务会大量的更新、插入或者删除行,那么适当的增大 Log Buffer 的大小,也可以有效的减少部分磁盘 I/O 操作。
至于 Log Buffer 中的数据刷入到磁盘的频率,则可以通过参数innodb_flush_log_at_trx_commit来决定。
本篇文章已放到我的 Github github.com/sh-blog 中,欢迎 Star。微信搜索关注【SH的全栈笔记】,回复【队列】获取MQ学习资料,包含基础概念解析和RocketMQ详细的源码解析,持续更新中。