mysql系列5—Innodb的缓存

背景

建议读者站在设计者的角度,思考如何设计InnoDB缓存

以[mysql系列3---mysql索引图解]中的聚簇索引为例, 查询主键为33的记录:

[1] 加载聚簇索引根节点所在的数据页;

[2] 使用33与目录(21, 35)依次进行比较,得到子节点信息(地址、表空间、页号);

[3] 加载子节点所在的数据页;

[4] 使用33与目录(30, 32, 33)依次进行比较,得到叶子节点信息(地址、表空间、页号);

[5] 将叶子节点中保存的数据返回;

此过程涉及3次IO操作,由于索引和数据存放于磁盘中,3次IO均为磁盘IO,速度较慢;再次执行相同查询,仍然要经历相同的3次磁盘IO。

另外,mysql规定磁盘与内存交换的单位为数据页(16K),即查询一条记录需加载记录所在的整个页面;且处于同一页面中的数据存在关联性,可能导致同一个页面被多次冗余加载。因此,可考虑引入缓存。

缓存的设计需要以数据库特性为前提,即需考虑如下几个问题:

[1] 缓存大小: 为保障数据库功能正常,需要限制缓存大小(不能无限扩大),可通过提供配置参数实现并提供合适的默认值。

[2] 缓存查询: 提供判断缓存是否已加载的能力,以及根据表空间和页序号从缓存中获取数据的能力。

[3] 缓存维护和淘汰策略: 缓存有大小限制,需要引入合适的淘汰机制,保证缓存的命中率。

[4] 缓存入库: 修改的数据需要在适当的时机入库。

本文将从这几个角度介绍InnoDB缓存。

1.Buffer pool

mysql服务器启动时,会根据innodb_buffer_pool_size 变量分配一块连续的内存空间用于缓存数据库数据,称为Buffer pool; 其中innodb_buffer_pool_size的默认值为134217728(128M):

mysql 复制代码
mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| innodb_buffer_pool_size | 134217728 |
+-------------------------+-----------+

mysql进一步将Buffer pool划分为一个个数据页(16K),并为每个数据页创建一个对应的控制块:

数据页用于缓存数据库数据; 控制块包含对数据页的管理信息,包括: 缓存页在Buffer pool的地址、表空间ID和数据页ID、lsn信息、链表节点信息(用于形成双向链表),控制块占据的空间远小于数据页。

有了Buffer pool后,可以将查询的数据页缓存到缓存页中,并在控制块中记录数据的表空间ID和数据页ID等信息。

在此基础上,建立一个哈希表用于加快缓存查询, 将表空间ID和数据页ID的组合作为key, 缓存页地址作为value。再次访问相同的数据页时,可以先判断哈希表是否包含对应的(表空间ID数据页ID),即数据页是否在Buffer pool中;如果包含,则根据(表空间ID数据页ID)从Buffer pool内存中加载数据,否则从磁盘根据地址加载数据页至内存。

上述缓存过程存在一个问题,如何得到一个空闲的缓存页?mysql引入free链为其提供了一个解决方案。

free链

因控制块与缓存页一一对应,且控制块中包含了缓存页在Buffer Pool的地址信息;可将空闲缓存页对应的控制块保存在一起,形成一个链表:

mysql启动时,由于所有的缓存页都未被使用,free链包含所有的控制块。

当从磁盘加载数据页缓存到Buffer Pool时,从free链可快速得到一个空闲的缓存页,将数据页数据加载到缓存页后,从free链路中删除对应的控制块。

缓存-1 被用于缓存数据后,free链的变化如下图所示:

flush链

数据页被加载到Buffer Pool后,mysql可对数据页进行读写;数据页被修改后就会与磁盘数据不一致,称为脏页。为保证内存与磁盘的一致性,需将脏页刷盘,而刷盘速度较慢;mysql选择对脏页进行标记,在后续的某个时间点批量同步到磁盘上。

标记的方式为将修改后的数据页对应的控制块添加到flush链路中。mysql启动时,没有数据页被修改,则flush链只有头节点,当修改缓存1时,变化如下:

2.lru链和淘汰策略

在第一章中介绍了Buffer Pool的结构以及free链和flush链,理解了如何加载和读取缓存页。由于Buffer Pool缓存大小是固定的,意味着需要不断地淘汰不常用的数据,以提高缓存的命中率,从而提高系统性能。本章需要考虑的问题是:确定适合的淘汰算法。

mysql特性

所谓合适是指需符合mysql的特性。

[1] 预加载

考虑到数据的相关性,从磁盘加载数据到缓存时,可进行预加载,即预先加载可能被读取的数据。mysql中有两种预加载:(1) 顺序访问某个区的页面超过阈值,触发读取下一个区的全部页面; (2) 如果某个区中连续多个页面被加载,页面数量超过阈值时,触发加载当前区的所有页面。

[2] 批量查询

mysql中可能存在大批量查询甚至全表查询情况,这些大批量的数据往往是低频使用的,易导致缓存大批量换血;且mysql的数据交换以页为单位,全表查询时,多条记录往往在同一个数据页中,会导致mysql误判数据页被频繁访问。

基于上述原因,mysql引入LRU链给出了一个解决方案。

LRU链

LRU链由两个部分组成,young区域和old区域,两个区域的节点具备流动性(类似Java的垃圾回收器)。

LRU链规则:

[1] 数据页第一次被加载到内存时,将控制块添加到Old区域头部;

[2] Old区域的页间隔innodb_old_blocks_time时间后再次被访问,则被认定为热点数据,移动到Yong区域头部;

[3] 位于Yong区域内的数据被访问时,移动到头节点; Yong前1/4的数据则不必移动,以减少修改频率。

LRU链涉及两个变量,可根据具体业务场景进行调优:

[1] innodb_old_blocks_pct控制OLD区域节点占据LRU整体的百分比,默认值为37:

mysql 复制代码
mysql> SHOW VARIABLES LIKE 'innodb_old_blocks_pct';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_old_blocks_pct | 37    |
+-----------------------+-------+

[2] innodb_old_blocks_time控制热点数据判定的时间间隔,单位毫秒:

mysql 复制代码
mysql> SHOW VARIABLES LIKE 'innodb_old_blocks_time';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| innodb_old_blocks_time | 1000  |
+------------------------+-------+

2.3 刷新脏页

将脏页刷新到磁盘的核心是选择脏页;mysql可以从flush中选择一部分数据刷入磁盘,也可以从lru的尾部选择一些脏页刷入磁盘。

**注意:**用户因查询数据而加载数据页进Buffer Pool时,如果没有足够的空间缓存,会同步处理数据释放问题,导致用户查询出现卡顿。通过调节Buffer pool大小,old区比例等参数,能一定程度减少卡顿问题出现的概率。

相关推荐
东软吴彦祖26 分钟前
包安装利用 LNMP 实现 phpMyAdmin 的负载均衡并利用Redis实现会话保持nginx
linux·redis·mysql·nginx·缓存·负载均衡
慵懒的猫mi1 小时前
deepin分享-Linux & Windows 双系统时间不一致解决方案
linux·运维·windows·mysql·deepin
小高不明2 小时前
仿 RabbitMQ 的消息队列2(实战项目)
java·数据库·spring boot·spring·rabbitmq·mvc
DZSpace2 小时前
使用 Helm 安装 Redis 集群
数据库·redis·缓存
张飞光2 小时前
MongoDB 创建集合
数据库·mongodb
Hello Dam2 小时前
接口 V2 完善:基于责任链模式、Canal 监听 Binlog 实现数据库、缓存的库存最终一致性
数据库·缓存·canal·binlog·责任链模式·数据一致性
张飞光2 小时前
MongoDB 创建数据库
数据库·mongodb·oracle
摘星怪sec3 小时前
【漏洞复现】|方正畅享全媒体新闻采编系统reportCenter.do/screen.do存在SQL注入
数据库·sql·web安全·媒体·漏洞复现
基哥的奋斗历程3 小时前
学到一些小知识关于Maven 与 logback 与 jpa 日志
java·数据库·maven
苏-言4 小时前
MyBatis最佳实践:提升数据库交互效率的秘密武器
数据库·mybatis