MySQL底层概述—4.InnoDB数据文件

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: 本文介绍了InnoDB表空间文件结构及其组成部分,包括表空间、段、区、页和行。表空间是最高逻辑层,包含多个段;段由若干个区组成,每个区包含64个连续的页,页用于存储多条行记录。文章还详细解析了Page结构,分为通用部分(文件头与文件尾)、数据记录部分和页目录部分。此外,文中探讨了行记录格式,包括四种行格式(Redundant、Compact、Dynamic和Compressed),重点介绍了Compact行记录格式及其溢出机制。最后,文章解释了不同行格式的特点及应用场景,帮助理解InnoDB存储引擎的工作原理。

大纲

1.表空间文件结构

(1)表空间Tablesapce

(2)段Segment

(3)区Extend

(4)页Page

(5)行Row

2.Page结构

(1)页结构各部分说明

(2)页结构整体划分

3.行记录格式

(1)行格式分类

(2)COMPACT行记录格式

(3)Compact中的行溢出机制

(4)其他行格式记录

 

1.表空间文件结构

(1)表空间Tablesapce

(2)段Segment

(3)区Extend

(4)页Page

(5)行Row

 

InnoDB表空间文件结构从逻辑上可以分为:

Tablespace(表空间)->Segment(段)->

Extent(区)->Page(页)->Row(行)

 

一个表空间会包含多个段,一个段会包含多个区(256个区就是一个组);一个区又会包含64个页,一个页里面又会包含一行一行的记录Row。

(1)表空间Tablesapce

一.表空间是什么

表空间能够看作是InnoDB存储引擎逻辑结构的最高层。表空间用于存储多个ibd数据文件,用于存储表的记录和索引。一个表空间文件可以包含多个段:叶子节点段、非叶子节点段、回滚段。

 

二.表空间类型

系统表空间、独占表空间、通用表空间、 临时表空间、Undo表空间。

 

(2)段Segment

一.段是什么

段是用来管理空间的申请以及将同类的区和页用链表管理起来。段是个逻辑概念,本质上是由若干个零散页面和若干个完整的区组成。段是为了保持叶子节点在磁盘上的连续,可以实现更好的顺序IO操作。

 

一个B+树索引被划分为两个段:一个叶子节点段和一个非叶子节点段。这样叶子节点就可以尽可能地存放在一起,非叶子节点也可以尽可能地存放在一起。

 

二.段的类型

常见的段有数据段、索引段、回滚段等。其中索引段就是非叶子节点部分,而数据段就是叶子节点部分,回滚段用于数据的回滚和多版本控制。

 

三.为什么引入段

原因一:

使用B+树执行查询时只是扫描叶子节点记录,如果不区分叶子节点和非叶子节点,通通把节点代表的页面放到申请的区中,那么扫描效果就大打折扣,而段可以让叶子节点的数据页尽可能连续和差距不那么大。所以InnoDB对B+树的叶子节点和非叶子节点进行区别对待,叶子节点和非叶子节点各有自己独有的区。而存放叶子节点的区的集合就算是一个段,存放非叶子节点的区的集合也算是一个段。即一个索引会生成两个段:一个叶子节点段和一个非叶子节点段。

 

原因二:

以完整的区为单位分配给某个段时,对于数据量较小的表来说太浪费存储空间。因为当段以区为单位申请存储空间时,由于一个区默认占用1MB存储空间以及一个聚簇索引会生成两个段,所以默认情况下只存放了几条记录的小表也需要2MB的存储空间,这就有点浪费了。

 

原因三:

出现上述问题的根源是:区中的所有页面都是为了存储同一个段的数据而存在,即使区的页面用不完也不能作他用。于是InnoDB便有了碎片区。在一个碎片区中,并非所有页都是为了存储同一个段的数据而存在,碎片区的页可以用于不同的目的的。比如有些页属于段A、有些页属于段B、有些页甚至不属于任何段。碎片区直属于表空间,不属于任何段。

 

原因四:

为某个段分配存储空间的策略:刚开始向表中插入数据时,段是从某个碎片区中以单个页面为单位来分配存储空间的。当某个段已经占用了32个碎片区页面后,就会以完整的区为单位来分配存储空间,原先占用的碎片区页面并不会被复制到新申请的完整的区中。所以说,段是一些零散的页面以及一些完整的区的集合。

 

(3)区Extend

一.区是什么

区由连续页组成的空间,一个区的大小是1M,一个区有64个连续的页。为了保证区中页的连续性,扩展时InnoDB一次从磁盘申请4~5个区。无论是系统表空间还是独立表空间,都可看成是由若干个连续的区组成。当一个段使用了32个碎片页后才是以区来分配,每256个区被分成一组。

 

二.为什么引入区

原因一:

向表中插入一条记录,本质上就是向该表的聚簇索引以及所有二级索引代表的B+树的节点中插入数据。而B+树每一层中的页都会形成一个双向链表,如果以页为单位来分配存储空间,那么双向链表中相邻的两个页之间的物理位置可能离得非常远。

 

原因二:

使用B+树来减少记录的扫描行数的过程是:通过一些搜索条件,到B+树的叶子节点中定位到第一条符合该条件的记录,然后沿着由记录组成的单向链表以及由数据页组成的双向链表,一直向后进行扫描。全表扫描就是定位到第一个叶子节点的第一条记录。

 

原因三:

如果双向链表中相邻的两个页的物理位置不连续,对于传统的机械硬盘来说,需要重新定位磁头位置,也就是会产生随机IO,影响性能。所以应尽量让页面链表中相邻的页的物理位置也相邻,以便扫描叶子节点的大量记录时可以使用顺序IO。

 

原因四:

为了尽量消除随机IO才引入了区的概念,一个区就是物理位置上连续的64个页,区中页面的页号都是连续的。当表中的数据量很大时,为某个索引分配空间时就不再按页为单位来分配了,而是按照区为单位进行分配。甚至当表中的数据非常非常多的时候,可以一次性分配多个连续的区,以消除更多的随机IO,但会造成一点空间的浪费。

 

(4)页Page

一.页是什么

区是由连续的页(Page)组成的空间,一个页的存储大小为16K,页用于存储多个Row行记录。

 

二.页的类型

页有很多种类型,如数据页、Undo页、系统页、事务数据页、大的BLOB对象页。

 

(5)行Row

InnoDB的数据是按行的方式进行存放的,每个页存放的行记录最多允许存放16K / 2 - 200行的记录,即每个页最多存放7992行记录。每行记录根据不同的行格式、不同的数据类型,会有不同的存储方式。

 

行包含的内容:记录的字段值、事务ID、回滚指针、字段指针等信息。

 

2.Page结构

(1)页结构各部分说明

(2)页结构整体划分

 

Page是InnoDB存储的最基本构件,也是InnoDB磁盘管理的最小单位,与数据库相关的所有内容都存储在这种Page结构里。

 

Page分为几种类型,常见的页类型有:数据页(B+Tree Node)、Undo页(Undo Log Page)、系统页(System Page)、事务数据页(Transaction System Page)等。

 

(1)页结构各部分说明

一.File Header字段用于记录Page的头信息

其中比较重要的是FIL_PAGE_PREV和FIL_PAGE_NEXT字段。通过这两个字段,就可以找到该页的上一页和下一页。实际上所有页通过两个字段可以形成一条双向链表。

二.Page Header字段用于记录Page的状态信息

三.Infimum和Supremum是最小和最大行记录

Infimum(下确界)记录比该页中任何主键值都要小的值,Supremum(上确界)记录比该页中任何主键值都要大的值,这两个伪记录构成了页中记录的边界。

四.User Records存放的是实际的数据行记录

五.Free Space中存放的是空闲空间

被删除的行记录会成为空闲空间。

六.Page Directory记录与二叉查找相关的信息

七.File Trailer存储检测数据完整性的数据

 

(2)页结构整体划分

页结构整体上可以分为三大部分,分别为:通用部分(文件头、文件尾)、数据记录部分、页目录部分。

 

一.通用部分(File Header&File Trailer)

通用部分主要指文件头和文件尾,将页的内容进行封装。通过文件头和文件尾校验的CheckSum方式可以确保页的传输是完整的,这时候就可以确认是否发生页断裂也就是页是否写失效了。

 

其中比较重要的是在文件头中的FIL_PAGE_PREV和FIL_PAGE_NEXT字段,通过这两个字段,可以找到该页的上一页和下一页,因此所有页可以形成一条双向链表。

 

二.数据记录部分(User Records&Free Space)

由于页的主要作用是存储记录,所以"最小和最大记录"和"用户记录"部分占了页结构的主要空间。另外空闲空间是个灵活的部分,当有新的记录插入时,会从空闲空间中进行分配用于存储新记录。

三.页目录部分(Page Directory)

数据页中的行记录会按照主键值由小到大顺序串联成一个单链表,单链表的链表头为最小记录,链表尾为最大记录。数据页目录中会顺序存储每一条行记录的地址,通过对数据页目录使用二分法,就能快速定位到查找的行记录。

 

3.行记录格式

(1)行格式分类

(2)COMPACT行记录格式

(3)Compact中的行溢出机制

(4)其他行格式记录

 

(1)行格式分类

表的行格式决定了它的行是如何物理存储的,这反过来又会影响查询和DML操作的性能。

 

如果在单个Page页中容纳更多行,那么查询和索引查找就能更快地工作,并且缓冲池中所需的内存会更少,写入更新时所需的IO也会更少。

 

InnoDB存储引擎支持四种行格式:Redundant、Compact、Dynamic和Compressed。

 

下面查询MySQL使用的行格式,MySQL5.7后默认是dynamic。

mysql> show variables like 'innodb_default_row_format';
+---------------------------+---------+
| Variable_name             | Value   |
+---------------------------+---------+
| innodb_default_row_format | dynamic |
+---------------------------+---------+

下面是指定行格式的语法:

CREATE TABLE <table_name(column_name)> ROW_FORMAT=行格式名称
ALTER TABLE <table_name> ROW_FORMAT=行格式名称

(2)COMPACT行记录格式

Compact设计目标是高效地存储数据,一个页中存放的行数据越多,其性能就越高。Compact行记录由两部分组成:记录的额外信息和记录的真实数据。

一.记录额外信息部分

服务器为了描述一条记录而添加了一些额外信息(元数据信息),这些额外信息分为3类,分别是:变长字段长度列表、NULL值列表和记录头信息。

 

第一类:变长字段长度列表

MySQL支持一些变长的数据类型,比如VARCHAR(M)、VARBINARY(M)、各种TEXT类型,各种BLOB类型。这些变长的数据类型占用的存储空间分两部分:真正的数据内容和占用的字节数。

 

变长字段的长度是不固定的,所以在存储数据时要把这些数据占用的字节数也存起来。读取数据时才能根据这个长度列表去读取对应长度的数据。

 

在Compact行格式中:会把所有变长类型的列的长度都存放在记录的开头部位形成一个列表,按照列的顺序逆序存放,这个列表就是变长字段长度列表。

 

第二类:NULL值列表

表中的某些列可能会存储NULL值,如果把这些NULL值都放到记录的真实数据中会比较浪费空间,所以Compact行格式把这些值为NULL的列存储到NULL值列表中。如果表中所有列都不允许为 NULL,就不存在NULL值列表。

 

第三类:记录头信息

记录头信息是由固定的5个字节组成,5个字节也就是40个二进制位,不同的位代表不同的意思。

delete_mask:这个属性标记着当前记录是否被删除,占用1个二进制位。值为0时代表记录并没有被删除,值为1时代表记录被删除掉。

 

min_rec_mask:标记该记录是否是B+树的每层非叶子节点中的最小记录。

 

n_owned:代表每个分组里,所拥有的记录的数量,一般是分组里主键最大值才有的。

 

heap_no:在数据页的User Records中插入的记录是一条条紧凑排列的,这种紧凑排列的结构又被称为堆。为了便于管理这个堆,把记录在堆中的相对位置给定一个编号heap_no,所以heap_no这个属性表示当前记录在本页中的位置。

 

record_type:这个属性表示当前记录的类型,一共有4种类型的记录。0表示普通用户记录、1表示B+树非叶节点记录、2表示最小记录、3表示最大记录。

 

next_record :表示从当前记录的真实数据到下一条记录的真实数据的地址偏移量,可以理解为指向下一条记录地址的指针。值为正数说明下一条记录在当前记录的后面,值为负数说明下一条记录在当前记录的前面。

 

二.记录真实数据部分

除了记录真实数据以外,MySQL还会为每条行记录添加一些列。这些列被称为隐藏列,具体的列如下:

列说明如下:

生成隐藏主键列的步骤:

 

步骤一:

服务器会在内存中维护一个全局变量,每当向某个包含隐藏的row_id列的表中插入一条记录时,就会把该变量的值当作新记录的row_id列的值,并且把该变量自增1。

 

步骤二:

每当这个变量的值为256的倍数时,就会将该变量的值,刷新到系统表空间的页号为7的页面中一个Max Row ID的属性中。

 

步骤三:

系统启动时会将页中的Max Row ID属性加载到内存中,并将该值加上256后赋值给全局变量,因为在上次关机时该全局变量的值可能大于页中Max Row ID属性值。

 

(3)Compact中的行溢出机制

一.什么是行溢出

MySQL中是以页为基本单位进行磁盘与内存之间的数据交互的。一个页的大小是16K = 16384字节。一个varchar(m)类型列最多可以存储65532个字节,一些大的数据类型如TEXT可以存储更多。如果一个表存在这样的大字段,那么一个页就无法存储一条完整的记录。这时就会发生行溢出,多出的数据就会存储在另外的溢出页中。

 

总结:如果某些字段信息过长,无法存储在B树节点中。这时候会被单独分配空间,此时被称为溢出页,该字段被称为页外列。

 

二.Compact中的行溢出机制

InnoDB规定一页至少存储两条记录(B+树特点),如果页中只能存放下一条记录,InnoDB会自动将行数据存放到溢出页中。当发生行溢出时,数据页只保存前768字节的前缀数据,接着是20个字节的偏移量,指向行溢出页。

(4)其他行格式记录

一.DYNAMIC和COMPRESSED行记录格式

DYNAMIC和COMPRESSED新格式引入的功能有:数据压缩、增强型长列数据的页外存储和大索引前缀。

 

Compressed和Dynamic行记录格式与Compact行记录格式是类似的。区别是在处理行溢出时:数据页不会存储真实数据的前768字节(完全溢出),而只存储20个字节的指针来指向溢出页。

Compressed与Dynamic相比:Compressed存储的行数据会以zlib的算法进行压缩以节省空间,因此对于BLOB、TEXT、VARCHAR这类大长度类型的数据能有效存储。MySQL5.7默认的行记录格式是Dynamic。

 

二.Redundant

Redundant是MySQL 5.0版本前InnoDB的行记录存储方式。Redundant行记录的格式是:首部是一个字段长度偏移列表,同样是按照列的顺序逆序放置的,该条记录中所有列的长度信息都按照逆序存储到字段长度偏移列表,这些列当然包括隐藏列、NULL值列等。

 

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
存储 缓存 关系型数据库
MySQL底层概述—9.ACID与事务
本文介绍了数据库事务的ACID特性(原子性、一致性、隔离性、持久性),以及事务控制的演进过程,包括排队、排它锁、读写锁和MVCC(多版本并发控制)。文章详细解释了每个特性的含义及其在MySQL中的实现方式,并探讨了事务隔离级别的类型及其实现机制。重点内容包括:ACID特性(原子性、持久性、隔离性和一致性的定义及其实现方式)、事务控制演进(从简单的全局排队到复杂的MVCC,逐步提升并发性能)、MVCC机制(通过undo log多版本链和Read View实现高效并发控制)、事务隔离级别(析了四种隔离级别(读未提交、读已提交、可重复读、可串行化)的特点及适用场景)、隔离级别与锁的关系。
|
6天前
|
存储 SQL 关系型数据库
【YashanDB知识库】MySQL迁移至崖山char类型数据自动补空格问题
**简介**:在MySQL迁移到崖山环境时,若字段类型为char(2),而应用存储的数据仅为&#39;0&#39;或&#39;1&#39;,查询时崖山会自动补空格。原因是mysql的sql_mode可能启用了PAD_CHAR_TO_FULL_LENGTH模式,导致保留CHAR类型尾随空格。解决方法是与应用确认数据需求,可将崖山环境中的char类型改为varchar类型以规避补空格问题,适用于所有版本。
|
1月前
|
SQL 关系型数据库 MySQL
MySQL底层概述—10.InnoDB锁机制
本文介绍了:锁概述、锁分类、全局锁实战、表级锁(偏读)实战、行级锁升级表级锁实战、间隙锁实战、临键锁实战、幻读演示和解决、行级锁(偏写)优化建议、乐观锁实战、行锁原理分析、死锁与解决方案
115 24
MySQL底层概述—10.InnoDB锁机制
|
1月前
|
缓存 算法 关系型数据库
MySQL底层概述—8.JOIN排序索引优化
本文主要介绍了MySQL中几种关键的优化技术和概念,包括Join算法原理、IN和EXISTS函数的使用场景、索引排序与额外排序(Using filesort)的区别及优化方法、以及单表和多表查询的索引优化策略。
104 22
MySQL底层概述—8.JOIN排序索引优化
|
3天前
|
缓存 NoSQL 关系型数据库
Redis和Mysql如何保证数据⼀致?
1. 先更新Mysql,再更新Redis,如果更新Redis失败,可能仍然不⼀致 2. 先删除Redis缓存数据,再更新Mysql,再次查询的时候在将数据添加到缓存中 这种⽅案能解决1 ⽅案的问题,但是在⾼并发下性能较低,⽽且仍然会出现数据不⼀致的问题,⽐如线程1删除了 Redis缓存数据,正在更新Mysql,此时另外⼀个查询再查询,那么就会把Mysql中⽼数据⼜查到 Redis中 1. 使用MQ异步同步, 保证数据的最终一致性 我们项目中会根据业务情况 , 使用不同的方案来解决Redis和Mysql的一致性问题 : 1. 对于一些一致性要求不高的场景 , 不做处理例如 : 用户行为数据 ,
|
1月前
|
SQL 关系型数据库 MySQL
MySQL底层概述—7.优化原则及慢查询
本文主要介绍了:Explain概述、Explain详解、索引优化数据准备、索引优化原则详解、慢查询设置与测试、慢查询SQL优化思路
123 15
MySQL底层概述—7.优化原则及慢查询
|
1月前
|
存储 关系型数据库 MySQL
MySQL底层概述—6.索引原理
本文详细回顾了:索引原理、二叉查找树、平衡二叉树(AVL树)、红黑树、B-Tree、B+Tree、Hash索引、聚簇索引与非聚簇索引。
MySQL底层概述—6.索引原理
|
1月前
|
存储 缓存 关系型数据库
MySQL底层概述—5.InnoDB参数优化
本文介绍了MySQL数据库中与内存、日志和IO线程相关的参数优化,旨在提升数据库性能。主要内容包括: 1. 内存相关参数优化:缓冲池内存大小配置、配置多个Buffer Pool实例、Chunk大小配置、InnoDB缓存性能评估、Page管理相关参数、Change Buffer相关参数优化。 2. 日志相关参数优化:日志缓冲区配置、日志文件参数优化。 3. IO线程相关参数优化: 查询缓存参数、脏页刷盘参数、LRU链表参数、脏页刷盘相关参数。
MySQL底层概述—5.InnoDB参数优化
|
25天前
|
SQL 关系型数据库 MySQL
基于SQL Server / MySQL进行百万条数据过滤优化方案
对百万级别数据进行高效过滤查询,需要综合使用索引、查询优化、表分区、统计信息和视图等技术手段。通过合理的数据库设计和查询优化,可以显著提升查询性能,确保系统的高效稳定运行。
46 9
|
1月前
|
监控 关系型数据库 MySQL
MySQL和SQLSugar百万条数据查询分页优化
在面对百万条数据的查询时,优化MySQL和SQLSugar的分页性能是非常重要的。通过合理使用索引、调整查询语句、使用缓存以及采用高效的分页策略,可以显著提高查询效率。本文介绍的技巧和方法,可以为开发人员在数据处理和查询优化中提供有效的指导,提升系统的性能和用户体验。掌握这些技巧后,您可以在处理海量数据时更加游刃有余。
94 9

相关产品

  • 云数据库 RDS MySQL 版
  • 推荐镜像

    更多