LRU(least recently used , 即最近最少使用)算法是数据库中缓冲池的管理算法,将数据库的缓冲池作为一个列表使用,该列表分为两个部分:New Sublist,Old Sublist。频繁使用的页在列表的‘新端’,而使用最少的页面在列表的‘旧端’。
Innodb中,同样使用LRU算法进行管理,与传统的LRU算法不同,Innodb存储引擎引入mitpoint位置(图中剪头所指),默认下,该位置处于列表的5/8处,因此该LRU算法默认的运行方式如下:
3/8的缓冲池专用于旧的子列表。
列表的中点是新子列表的尾部与旧子列表的头相交的边界。
当InnoDB将页面读入缓冲池时,它首先将其插入中点(旧子列表的头部)。可以读取页面,因为它是用户启动的操作(例如SQL查询)所必需的,或作为的自动执行的预读操作的一部分 InnoDB。
访问旧子列表中的页面 使其变为“ 年轻 ”,将其移至新子列表的头部。如果由于用户启动的操作而需要读取页面,则将立即进行首次访问,并使页面年轻。如果由于预读操作而读取了该页面,则第一次访问不会立即发生,并且在退出该页面之前可能根本不会发生。
随着数据库的运行,通过移至列表的尾部,缓冲池中未被访问的页面将“ 老化 ”。新的和旧的子列表中的页面都会随着其他页面的更新而老化。随着将页面插入中点,旧子列表中的页面也会老化。最终,未使用的页面到达旧子列表的尾部并被逐出。
mitpoint的位置可以由参数参数innodb_old_blocks_pct控制,
[root@jt-backup ~]# mysql -e "show variables like 'innodb_old_blocks_pct';"
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_old_blocks_pct | 37 |
+-----------------------+-------+
那为什么不采用朴素的LRU算法,直接将读取的页放入到LRU列表的首部呢?这是因为若直接将读取到的页放入到LRU的首部,那么某些SQL操作可能会使缓冲池中的页被刷新出,从而影响缓冲池的效率。常见的这类操作为索引或数据的扫描操作。这类操作需要访问表中的许多页,甚至是全部的页,而这些页通常来说又仅在这次查询操作中需要,并不是活跃的热点数据。如果页被放入LRU列表的首部,那么非常可能将所需要的热点数据页从LRU列表中移除,而在下一次需要读取该页时,InnoDB存储引擎需要再次访问磁盘。(摘自:《MySQL技术内幕:InnoDB存储引擎(第2版)》 — 姜承尧)
为了防止缓冲池中的热点数据被类似索引扫描页类操作刷新出去,配置参数 innodb_old_blocks_time 指定第一次访问页面后的时间窗口(以毫秒为单位),在该时间窗口内可以访问页面而不将其移到LRU列表的最前面(最近使用的末尾)。默认值 innodb_old_blocks_time是 1000。增加此值会使越来越多的块从缓冲池中更快地老化。
[root@jt-backup ~]# mysql -e "show variables like 'innodb_old_blocks_time';"
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| innodb_old_blocks_time | 1000 |
+------------------------+-------+
网友评论