mysql系列5—Innodb的缓存

embedded/2024/12/27 0:22:00/

背景

建议读者站在设计者的角度,思考如何设计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区比例等参数,能一定程度减少卡顿问题出现的概率。


http://www.ppmy.cn/embedded/149032.html

相关文章

政务上云建设方案和总体技术解决方案(资料原件)

1、政务上云模式选择 2、政务业务上云评估 3、政务云可行建设步骤 4、政务系统上云策略 5、政务系统上云流程 6、政务云平台架构 7、政务云平台逻辑架构设计 8、政务云安全建设框架 9、政务云安全技术要求 10、政务云安全分区 11、政务云灾备架构设计 12、政务云运维运营平台 软…

使用VSCode Debugger 调试 React项目

一般我们调试代码时,用的最多的应该就是console.log方式了,还有的是使用Chrome DevTools 通过在对应的 sourcemap代码位置打断点进行调试,除了上面两种方式外还有一种更好用的调试方式: VSCode Debugger。 VSCode Debugger可以直…

Java基础面试题17:GenericServlet和HttpServlet有什么区别?

Java基础面试题:GenericServlet和HttpServlet有什么区别? 1. 什么是GenericServlet? GenericServlet 是一个“通用型”的Servlet,它跟具体的协议(比如HTTP)无关。简单来说,它提供了一种框架&a…

【工作小记】Spring MVC框架中的线程安全分析

Spring MVC框架中的线程安全问题是很多开发人员都会遇到的一种情况。那么,如何保证线程安全呢?下面将从三个方面来进行分析:单例模式的应用、ThreadLocal的使用以及无状态Controller的设计。 首先,我们来看单例模式。在Spring MVC…

方格分割(蓝桥杯2017年试题D)

【问题描述】 6*6的方格,沿着格子的边线剪开成两部分,要求这两部分的形状完全相同。如下图所示,p1.png、p2.png、p3.png就是可行的分割法。 试计算:包括这三种分法在内,一共有多少种不同的分割方法。 注意:…

基于Java的在线教育系统

一、系统背景与意义 随着互联网技术的不断发展和普及,在线教育已经成为一种新兴的教育模式,它突破了时间和空间的限制,使得人们可以随时随地接受教育。基于Java的在线教育系统正是为了适应这种需求而开发的,它利用Java语言的跨平…

汽车IVI中控开发入门及进阶(42):OpenVG

概览: OpenVG是一个无版权、跨平台的API,它为高级用户界面和矢量图形库(如SVG)提供了一个低级硬件加速接口。OpenVG主要针对需要便携式加速高质量矢量图形以获得引人注目的用户界面和文本的消费电子产品、手持设备、可穿戴设备和汽车设备,同时使硬件加速能够在非常低的功…

机器学习Python使用scikit-learn工具包详细介绍

一、简介 Scikit-learn是一个开源的机器学习库,用于Python编程语言。它建立在NumPy、SciPy和matplotlib这些科学计算库之上,提供了简单有效的数据挖掘和数据分析工具。Scikit-learn库包含了许多用于分类、回归、聚类和降维的算法,包括支持向量…