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),并为每个数据页创建一个对应的控制块:![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/a91f06b1013b49e491a9f45996a8ac2e.jpeg) 数据页用于缓存数据库数据; 控制块包含对数据页的管理信息,包括: 缓存页在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的地址信息;可将空闲缓存页对应的控制块保存在一起,形成一个链表: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/bf989accd15e46aeaba692e7199904ec.jpeg) mysql启动时,由于所有的缓存页都未被使用,free链包含所有的控制块。 当从磁盘加载数据页缓存到Buffer Pool时,从free链可快速得到一个空闲的缓存页,将数据页数据加载到缓存页后,从free链路中删除对应的控制块。 如**缓存-1** 被用于缓存数据后,free链的变化如下图所示: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/1b93b79fb35b44099914c534d33de203.jpeg) **flush链** 数据页被加载到Buffer Pool后,mysql可对数据页进行读写;数据页被修改后就会与磁盘数据不一致,称为脏页。为保证内存与磁盘的一致性,需将脏页刷盘,而刷盘速度较慢;mysql选择对脏页进行标记,在后续的某个时间点批量同步到磁盘上。 标记的方式为将修改后的数据页对应的控制块添加到flush链路中。mysql启动时,没有数据页被修改,则flush链只有头节点,当修改缓存1时,变化如下: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/f16992c19cbc49ef851f0a1936e313eb.jpeg) ## 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的垃圾回收器)。 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/fbdb4418768745dc89cd2797d339da82.jpeg) 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区比例等参数,能一定程度减少卡顿问题出现的概率。

相关推荐
@淡 定1 小时前
MySQL MVCC 机制解析
数据库·mysql
Chandler241 小时前
Redis:内存淘汰原则,缓存击穿,缓存穿透,缓存雪崩
数据库·redis·缓存
SRC_BLUE_171 小时前
Python GUI 编程 | QObject 控件基类详解 — 定时器
开发语言·数据库·python
DBWYX1 小时前
MySQL 进阶 面经级
数据库·mysql
喝醉酒的小白2 小时前
SQL Server:触发器
数据库
靠近彗星3 小时前
基于 Vue + Django + MySQL 实现个人博客/CMS系统
前端·vue.js·python·mysql·django
银河金融数据库3 小时前
历史分钟高频数据
数据库·金融
男Ren、麦根3 小时前
MySQL 复制与主从架构(Master-Slave)
数据库·mysql·架构
Foyo Designer4 小时前
【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 中的国际化:支持多语言的 RESTful API
java·spring boot·redis·后端·spring·缓存·restful
Stuild Stuil4 小时前
Mysql 字段值批量自增或自减(坐标系数据,(x,y))
java·javascript·mysql