MySQL之深入InnoDB存储引擎——Buffer Pool

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。在数据库系统中,由于CPU速度与磁盘速度之间的鸿沟,基于磁盘的数据库系统通常使用缓冲池技术来提高数据库的整体性能。在数据库中进行读取页的操作,首先将从磁盘读到的页存放在缓冲池中,这个过程称为将页“FIX”在缓冲池中,在下一次读取相同的页时,首先判断该页是否存在缓冲池中,如果存在则被命中,直接读取,否则读取磁盘上的页。

一、空闲链表的管理

在我们最开始启动MySQL服务器的时候,需要向操作系统申请Buffer Pool的内存空间,然后把它换分成若干对控制块和缓冲页。当然此时没有真实的磁盘页被缓存到Buffer Pool中,会随着程序的运行不断缓存。


当从磁盘上读取到一个页时,我们需要将其放置在空闲的缓冲页中。那我们就需要区分哪些页是空闲的,哪些页已经被使用了,因此引入了Free链表(空闲链表)。在Buffer Pool刚刚完成初始化之后,所有缓冲页对应的控制块都会加入到空闲链表中。而为了管理这个空闲链表,我们为其定义了一个基节点,其中包含了链表的头节点地址、尾节点地址以及当前链表中节点的数量等信息。需要注意的是,链表的基节点占用的内存空间并不包含在为Buffer Pool申请的一大片连续内存孔内,而是一块单独申请的内存空间。


有了空闲链表之后,每当需要从磁盘中加载一个页到Buffer Pool中时,就从空闲链表中取一个缓冲页对应的控制块,把页加载到控制块指向的缓冲页中,并把该控制块的信息给填上(即表空间、页号等)。


二、缓冲页的哈希处理

使用了Buffer Pool之后,我们访问某个页的数据时会先去缓冲池中查找该页是否已经加载到内存中,如果不存在才会去磁盘中加载。而为了快速判断目标页是否存在于缓冲池中,InnoDB采用的是哈希表的方式,将表空间号和页号作为键,缓冲页控制块作为值来进行构建。


三、Flush链表的管理

如果我们修改了Buffer Pool中某个缓冲页的数据,那其中的数据就和磁盘中的不同了,此时这个缓冲页变为了脏页。虽然我们可以在每次修改完都将其刷新到磁盘中对应的页上,但是这显然会严重影响程序的性能。因此InnoDB不会在每次修改完页都立刻刷库,而是在未来某个时间点进行刷新。


既然不是及时刷新,那我们在未来刷新时便需要知道哪些页是脏页。因此引入了Flush链表进行脏页的管理,当缓冲页被修改则将其控制块加入到链表中。


和空闲链表相同,它也有一个相同结构的基节点来保存这个链表的信息。显然当一个缓冲页的控制块在空闲链表中时则不可能在Flush链表中,反之同理。


四、LRU链表的管理

Buffer Pool对应的内存大小毕竟是有限的,随着程序的运行Free链表总归会用完。如果Free链表已经没有多余的空闲缓冲页了,那么只能将某些旧的缓冲页从Buffer Pool中淘汰,再将新的页放进来。Buffer Pool引入的初衷是为了想减少磁盘IO,因此自然希望淘汰掉的页面是最少使用的,而最近经常使用的页面不被淘汰,因此InnoDB采用的淘汰策略为LRU,即最近最少使用算法。以这个算法为核心,InnoDB维护了一个LRU链表,如果页不在Buffer Pool中,在把该页从磁盘加载到Buffer Pool的时候,就把该缓冲页对应的控制块作为节点添加到LRU链表的头部,如果使用到链表中的某个页时也将其移动到链表头部。之后当空闲缓冲页用完时,就从链表尾部进行淘汰。


但这种实现并不完善,它存在两个问题:


1.因为InnoDB提供了预读的功能,即InnoDB认为执行当前的请求时,可能会在后面读取某些页面,于是就预先把这些页面加载到Buffer Pool中。根据触发方式的不同,预读可分为以下两种:


  • 线性预读:InnoDB中有一个系统变量innodb_read_ahead_threshold,如果顺序访问某个区的页面超过这个系统变量的值,则会触发一次异步读取下一个区中所有页面的请求
  • 随机预读:如果某个区的13个连续的页面都被加载到了Buffer Pool中,无论这些页面是不是顺序读取的,都会触发一次异步读取本区中所有其他页面到Buffer Pool中的请求。这个功能默认不会开启
  • 预读到Buffer Pool中的页如果能够被成功的使用到,那自然可以极大地提高语句执行的效率。但是如果用不到,会导致LRU链表尾部的一些缓冲页很快就被淘汰掉,从而大大降低Buffer Pool的命中率


2.有的时候可能需要用到全表扫描,这个过程中需要访问的页面一般会特别多,那么就会导致LRU链表中很多页面都会被淘汰掉。但是这次全表扫描引进来的这些页面可能并不是热点数据,但是反而把热点数据从链表中赶跑了


为了避免以上两种情况把Buffer Pool中真正的热点数据淘汰,InnoDB将LRU分成两个部分:


  1. 一部分存储使用频率非常高的缓冲页,称为热数据或young数据
  2. 一部分存储使用频率不是很高的缓冲页,称为冷数据或old数据


LRU的划分是根据比例的,默认情况下old区域占LRU链表的37%。之后当磁盘的页面被初次加载到Buffer Pool中时,该缓冲页对应的控制块会放置到old区域的头部,而不再是整个链表的头部,这样就不会使得预读加载进来的页影响young区域中使用比较频繁的缓冲页。并且在对某个处于old区域的缓冲页进行第一次访问时,会在它对应的控制块中记录下整个访问时间,如果后续的访问时间与第一次访问的时间在某个时间间隔内,那么该页面就不会从old区域移动到young区域头部。这样一来也可以防止全表扫描时除了将数据页加载到Buffer Pool,还会频繁读取页面中的记录导致仍然会淘汰掉热点数据的情况。


除了以上的改进,LRU还有进一步的优化策略。对于young区域的缓冲页来说,如果我们每次访问一个缓冲页都需要把它移动到LRU链表的头部,其实开销是比较大的,因为young区域都是热点数据。因此InnoDB规定只有被访问的缓冲页位于young区域1/4的后面时,才会被移动到LRU链表的头部。


五、脏页刷新

后台有专门的线程负责每隔一段时间就把脏页刷新到磁盘,这样可以不影响用户线程处理正常的请求,刷新的方式主要有两种:


  1. 从LRU链表的冷数据中刷新一部分页面到磁盘:后台线程会定时从LRU链表尾部开始扫描一些页面。如果在LRU链表中发现脏页(缓冲页的控制块就会记录该缓冲页是否被修改),则把它们刷新到磁盘
  2. 从Flsuh链表中刷新一部分到磁盘:后台线程也会定时从Flush链表中刷新一部分页面到磁盘,刷新的速率取决于当时系统是否繁忙


当有线程在准备加载一个磁盘页到Buffer Pool中时没有可用的缓冲页时会尝试查看LRU链表尾部,看是否存在可以直接释放掉的未修改缓冲页。有时后台线程刷新脏页的进度比较慢,导致LRU链表尾部的脏页还没有被刷新,此时则不得不将LRU链表尾部的一个脏页同步刷新到磁盘中,这会降低用户请求的速度。


六、多Buffer Pool实例

在多线程环境下,访问Buffer Pool中的各个链表都需要加锁处理。在Buffer Pool特别大并且多线程并发量特别高的情况下,单一的Buffer Pool可能会影响请求的处理速度。所以在Buffer Pool特别大时,可以把它们拆分成若干个小的Buffer Pool,每个Buffer Pool都称为一个实例。它们之间是相互独立的,多线程并发时不会互相影响。

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
14天前
|
存储 缓存 关系型数据库
【MySQL进阶篇】存储引擎(MySQL体系结构、InnoDB、MyISAM、Memory区别及特点、存储引擎的选择方案)
MySQL的存储引擎是其核心组件之一,负责数据的存储、索引和检索。不同的存储引擎具有不同的功能和特性,可以根据业务需求 选择合适的引擎。本文详细介绍了MySQL体系结构、InnoDB、MyISAM、Memory区别及特点、存储引擎的选择方案。
【MySQL进阶篇】存储引擎(MySQL体系结构、InnoDB、MyISAM、Memory区别及特点、存储引擎的选择方案)
|
19天前
|
存储 关系型数据库 MySQL
MySQL存储引擎详述:InnoDB为何胜出?
MySQL 是最流行的开源关系型数据库之一,其存储引擎设计是其高效灵活的关键。InnoDB 作为默认存储引擎,支持事务、行级锁和外键约束,适用于高并发读写和数据完整性要求高的场景;而 MyISAM 不支持事务,适合读密集且对事务要求不高的应用。根据不同需求选择合适的存储引擎至关重要,官方推荐大多数场景使用 InnoDB。
64 7
|
28天前
|
存储 关系型数据库 MySQL
Mysql索引:深入理解InnoDb聚集索引与MyisAm非聚集索引
通过本文的介绍,希望您能深入理解InnoDB聚集索引与MyISAM非聚集索引的概念、结构和应用场景,从而在实际工作中灵活运用这些知识,优化数据库性能。
126 7
|
1月前
|
存储 关系型数据库 MySQL
MySQL引擎InnoDB和MyISAM的区别?
InnoDB是MySQL默认的事务型存储引擎,支持事务、行级锁、MVCC、在线热备份等特性,主索引为聚簇索引,适用于高并发、高可靠性的场景。MyISAM设计简单,支持压缩表、空间索引,但不支持事务和行级锁,适合读多写少、不要求事务的场景。
60 9
|
16天前
|
存储 Oracle 关系型数据库
数据库传奇:MySQL创世之父的两千金My、Maria
《数据库传奇:MySQL创世之父的两千金My、Maria》介绍了MySQL的发展历程及其分支MariaDB。MySQL由Michael Widenius等人于1994年创建,现归Oracle所有,广泛应用于阿里巴巴、腾讯等企业。2009年,Widenius因担心Oracle收购影响MySQL的开源性,创建了MariaDB,提供额外功能和改进。维基百科、Google等已逐步替换为MariaDB,以确保更好的性能和社区支持。掌握MariaDB作为备用方案,对未来发展至关重要。
42 3
|
16天前
|
安全 关系型数据库 MySQL
MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!
《MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!》介绍了MySQL中的三种关键日志:二进制日志(Binary Log)、重做日志(Redo Log)和撤销日志(Undo Log)。这些日志确保了数据库的ACID特性,即原子性、一致性、隔离性和持久性。Redo Log记录数据页的物理修改,保证事务持久性;Undo Log记录事务的逆操作,支持回滚和多版本并发控制(MVCC)。文章还详细对比了InnoDB和MyISAM存储引擎在事务支持、锁定机制、并发性等方面的差异,强调了InnoDB在高并发和事务处理中的优势。通过这些机制,MySQL能够在事务执行、崩溃和恢复过程中保持
45 3
|
16天前
|
SQL 关系型数据库 MySQL
数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog
《数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog》介绍了如何利用MySQL的二进制日志(Binlog)恢复误删除的数据。主要内容包括: 1. **启用二进制日志**:在`my.cnf`中配置`log-bin`并重启MySQL服务。 2. **查看二进制日志文件**:使用`SHOW VARIABLES LIKE 'log_%';`和`SHOW MASTER STATUS;`命令获取当前日志文件及位置。 3. **创建数据备份**:确保在恢复前已有备份,以防意外。 4. **导出二进制日志为SQL语句**:使用`mysqlbinlog`
59 2
|
30天前
|
关系型数据库 MySQL 数据库
Python处理数据库:MySQL与SQLite详解 | python小知识
本文详细介绍了如何使用Python操作MySQL和SQLite数据库,包括安装必要的库、连接数据库、执行增删改查等基本操作,适合初学者快速上手。
200 15
|
23天前
|
SQL 关系型数据库 MySQL
数据库数据恢复—Mysql数据库表记录丢失的数据恢复方案
Mysql数据库故障: Mysql数据库表记录丢失。 Mysql数据库故障表现: 1、Mysql数据库表中无任何数据或只有部分数据。 2、客户端无法查询到完整的信息。
|
30天前
|
关系型数据库 MySQL 数据库
数据库数据恢复—MYSQL数据库文件损坏的数据恢复案例
mysql数据库文件ibdata1、MYI、MYD损坏。 故障表现:1、数据库无法进行查询等操作;2、使用mysqlcheck和myisamchk无法修复数据库。