当前位置: 首页 > news >正文

Mysql深入学习:InnoDB执行引擎篇

InnboDB存储引擎

mysql大概的架构如下

第1层:连接层

主要负责对客户端发起的连接请求进行权限验证,并将连接的相关信息保存到连接池中,方便下次复用连接资源。
第2层:Server服务层

提供包括NoSQL和SQL接口的支持,负责SQL语句的解析、优化以及缓存等核心组件的处理。
第3层:存储引擎层

该层支持多种可插拔式存储引擎,负责完成数据的读写操作,并与磁盘中的数据文件和日志文件进行交互。用户可以根据实际需求选择不同的存储引擎。
第4层:文件系统层

此层涵盖了具体的数据文件、日志文件以及MySQL运行所需的程序文件。

在上述四层结构中,MySQL的核心架构由两大关键部分组成:服务层(Server Layer)与存储引擎层(Storage Engine Layer)。其中,服务层承担主要的逻辑处理任务,而存储引擎层则专注于底层的数据存储与访问操作。

一条sql执行的总体流程

一条sql语句的执行流程大概可以分为下面几个步骤

Client层:接收用户提交的SQL语句,并将查询结果返回给用户进行展示。
Server层:负责对SQL语句进行语法检查、语言解析、优化处理与执行操作,最终将执行结果反馈给客户端。
连接器:主要完成用户身份验证与权限控制,同时建立并管理与客户端的连接。
缓存模块:用于保存查询结果,当相同请求再次出现时,优先返回命中的缓存结果以提升查询效率。
分析器:负责对SQL语句进行词法和语法上的解析,形成抽象语法树。
优化器:制定执行策略,生成高效的执行计划,并确定是否使用索引进行数据检索。
执行器:调用底层存储引擎(如 InnoDB)进行实际数据操作,并通过文件系统返回结果集。

文件系统层:对数据进行持久化

MySql中SQL语句核心执行流程如下:


SQL语句输入 → 解析阶段(包括词法分析 → 语法分析 → 语义分析)→ 构建抽象语法树(AST) → 预处理阶段(进一步语义检查)→ 优化器阶段(逻辑优化 → 物理优化) → 生成物理查询计划 → 执行引擎执行 → 将结果返回客户端。

具体关键步骤如下

  • 词法分析:将完整的SQL语句拆解为一个个最小的语义单元(如字段名、操作符、关键字等),形成Token列表;
  • 语法分析:检查Token序列是否满足SQL语法规则,并基于此构造抽象语法树(AST),用于表达SQL的结构;
  • 预处理阶段:按照MySQL内部规则进一步分析语法树的合法性,例如检查字段是否存在、别名是否重复等;
  • 逻辑优化:在语义层面上对SQL进行优化,例如重写嵌套查询、合并表达式、移除无效字段等,输出逻辑执行计划;
  • 物理优化:基于逻辑计划选择具体的物理执行方式,如索引选择、连接策略、数据访问路径等,生成最终物理执行计划;
  • 执行阶段:执行器将物理计划转译为可运行指令(可能是字节码或底层操作),驱动数据库系统完成数据处理,并将结果传回客户端。

InnoDB存储引擎

在 MySQL 中,数据最终是保存在磁盘上的,具体存储到哪些文件中,则取决于所使用的存储引擎。

MySQL 支持多种存储引擎,不同引擎在数据组织方式上各有差异。其中 InnoDB 是最常见且默认启用的存储引擎,下面重点介绍其存储结构。InnoDB 的整体结构分为以下四个层级:

  1. 表空间(Tablespace):这是管理表数据的最顶层逻辑单位,由多个段构成,通常对应一个或多个磁盘上的物理数据文件。
  2. 段(Segment):表空间中的组成部分,逻辑上表示一类数据集合,如数据段、索引段等。每个段由若干个“区”构成。
  3. 区(Extent):这是 InnoDB 分配空间的基本单位,默认大小为 1MB,由连续的 64 个页面组成,目的是提升分配效率,避免频繁的单页申请。
  4. 页(Page):最小的磁盘读写单元,默认大小为 16KB,用于存放具体的表数据或索引信息。InnoDB 在内存与磁盘之间的读写操作就是以页为单位进行的。

在以上四级结构中,最核心的是“页”的设计。虽然数据保存在磁盘上,但 InnoDB 的实际处理过程发生在内存中。为了提升性能,InnoDB 并不是对单条记录进行操作,而是以整个“页”为单位进行读取和写入。页的大小由参数 innodb_page_size 决定,常见设置包括 64KB、32KB、16KB(默认值)、8KB 和 4KB。也就是说,每次读写操作的最小粒度就是一页的大小。

总结一句话:InnoDB 的存储层级结构就像一本书:书本(表空间)→ 章节(段)→ 小节(区)→ 页面(页),而实际的数据就像书页上的文字,真正的内容就记录在这些页中。

InnoDB行格式

在 InnoDB 中,行数据在磁盘上的存储结构被称为行格式。目前,InnoDB 支持以下 四种行格式

  1. Compact
  2. Redundant
  3. Dynamic
  4. Compressed

你可以通过以下 SQL 语句来设置或查看表的行格式

-- 创建表时指定行格式
CREATE TABLE 表名 (列定义) ROW_FORMAT=行格式名称;-- 修改表的行格式
ALTER TABLE 表名 ROW_FORMAT=行格式名称;-- 查看表当前使用的行格式
SHOW TABLE STATUS LIKE '表名';

不同 MySQL 版本下的默认行格式:

  • MySQL 5.0 以前:默认采用 Redundant
  • MySQL 5.0 到 5.7:默认为 Compact
  • MySQL 5.7 以后:默认为 Dynamic

Compact 行格式(使用最广泛)

Compact 行格式主要包含三部分额外信息:

  1. 变长字段长度列表
    记录每个变长字段(如 VARCHAR、TEXT 等)的实际长度,逆序排列,只记录非 NULL 字段的长度。
  2. NULL 值列表
    管理所有允许为 NULL 的列,每位用 1 表示为 NULL,0 表示非 NULL
    若表中没有 NULL 字段,该部分会被省略。

作用:NULL 值集中存储,避免在数据区中浪费存储空间。

举例说明
若有三列 col1, col2, col3 均允许为 NULL,插入 (NULL, 'abc', NULL)
NULL 值位图约为 101(二进制)

  1. 记录头信息(Record Header)
    用于描述记录的状态及结构,常见字段如下:

字段名

位数

描述

预留位1

1

未使用

预留位2

1

未使用

delete_mask

1

标识记录是否被删除

min_rec_mask

1

标记是否为非叶子节点中的最小记录(用于 B+ 树)

n_owned

4

当前记录拥有的记录数(页目录使用)

heap_no

13

在当前页中的记录位置

record_type

3

记录类型(0 普通记录,1 B+树节点,2 最小,3 最大)

next_record

16

下一条记录的偏移量


Compressed 行格式

Dynamic 行格式的基础上,增加了zlib 压缩机制,用于压缩大字段(如 BLOB、TEXT 等),可节省磁盘空间。
但由于压缩与解压操作较重,会影响读写性能,因此一般不推荐用于对性能要求高的业务场景。


Redundant 行格式(旧格式)

Redundant 是 MySQL 5.0 之前的默认行格式,目前已较少使用。

Compact 的主要区别在于:

  • 使用 偏移地址表 来代替变长字段长度表和 NULL 列位图;
  • 记录头信息中新增 n_field1byte_offs_flag 字段;
  • 缺失 record_type 字段。

Dynamic 行格式

这是 MySQL 5.7 以后的默认行格式,与 Compact 相似,最大区别在于:

  • Compact:会存储字段前 768 字节 + 20 字节指向溢出页的地址;
  • Dynamic:只保存 20 字节的溢出页地址,不保留字段前缀数据。

行溢出机制详解

当某列(如 TEXT 或 BLOB)超过一定大小限制时,InnoDB 不会将所有数据直接存入当前页,而是将其拆分:

  • 当前页中保存前 768 字节内容(Compact 行格式)或仅保存地址(Dynamic 行格式);
  • 超出部分存储在其他页中,这些被称为 溢出页(Uncompressed BLOB Page)

行溢出的临界点计算:

InnoDB 规定每个页至少容纳两条记录,并预留约 132 字节的系统信息,加上每条记录额外的 27 字节开销。
单列数据小于约 8099 字节时通常不会发生溢出。
若包含多列,该阈值会相应减少。

InnoDB数据页格式

在 InnoDB 中,一个 数据页大小为 16KB,被划分为多个功能区域,各部分结构如下:

区域名称

描述

大小

File Header

页的通用信息

38 字节

Page Header

页的专有信息

56 字节

Infimum + Supremum

最小/最大伪记录

26 字节

User Records

实际存储的数据记录

可变

Free Space

尚未使用的空闲空间

可变

Page Directory

页内记录的索引结构

可变

File Trailer

用于校验页完整性

8 字节

每当插入新记录时,会从 Free Space 区 中分配空间,将数据写入 User Records 区域
当 Free Space 被填满时,意味着当前页已写满,后续写入操作将触发分配新数据页。


📄 示例:插入记录
CREATE TABLE test(a1 INT,a2 INT,a3 VARCHAR(100),PRIMARY KEY (a1)
) CHARSET=ascii ROW_FORMAT=Compact;INSERT INTO test VALUES (1, 10, 'aaa');
INSERT INTO test VALUES (2, 20, 'bbb');
INSERT INTO test VALUES (3, 30, 'ccc');
INSERT INTO test VALUES (4, 40, 'ddd');

这些记录将按主键顺序被写入 User Records 部分。


InnoDB 记录头字段说明

InnoDB 中每条记录前都有一段记录头信息(Record Header),描述该条记录的状态,主要字段如下:

  1. delete_mask:记录删除状态,1 表示被删除(尚未物理清除),0 表示有效记录。已删除记录会进入垃圾链表,空间可复用。
  2. min_rec_mask:仅在 非叶子节点的最小记录中设置为 1,用于 B+ 树快速定位最小值。
  3. n_owned:一个页被划分成多个分组,每组的最后一条记录会记录该组中总记录数。
  4. heap_no:记录在页中的物理位置编号,从 2 开始计数(0 为 Infimum,1 为 Supremum)。
  5. record_type:记录的类型,含义如下:
    • 0:普通记录
    • 1:B+树的非叶子记录
    • 2:Infimum(最小伪记录)
    • 3:Supremum(最大伪记录)
  1. next_record:指向下一条记录的偏移量,形成按主键排序的链表结构。
    例如,next_record = 32 表示下条记录在当前记录偏移 32 字节的位置。

因此,记录结构是一个有序的链表:
Infimum → 最小主键 → ... → 最大主键 → Supremum


Page Directory(页目录结构)

虽然记录形成了有序单链表,方便插入与删除,但查找效率不高。为此,InnoDB 引入了 页目录(Page Directory),提高查找效率。

工作机制如下:
  1. 分组逻辑
    所有有效记录(不含已删除)被划为多个组,每组最后一条记录的 n_owned 表示本组记录数。每个组的最后一条记录地址作为一个“槽(slot)”,方便查找。
  2. 分组规则
    • 第一组(最小记录组)只能包含一条记录
    • 最后一组可容纳 1~8 条
    • 中间组保持 4~8 条记录
  1. 动态调整
    初始状态仅有 Infimum 和 Supremum 两个伪记录;
    插入新记录后,根据插入位置动态更新分组与 slot;
    若某组记录数超过 8 条,会被拆分成两个新组(例如 4 条 + 5 条)。

进一步插入记录(构造多组目录)
INSERT INTO test VALUES (5, 50, 'eee');
INSERT INTO test VALUES (6, 60, 'fff');
INSERT INTO test VALUES (7, 70, 'ggg');
INSERT INTO test VALUES (8, 80, 'hhh');
INSERT INTO test VALUES (9, 90, 'iii');
INSERT INTO test VALUES (10, 100, 'jjj');
INSERT INTO test VALUES (11, 110, 'kkk');
INSERT INTO test VALUES (12, 120, 'lll');

此时页内形成多个记录组,并自动生成多个 slot,以支持高效查找。


查找记录的过程(主键 = 6)

查找流程如下:

  1. 二分查找目录槽:根据主键大小,在 slot 中二分查找缩小范围。
    假设槽 1 对应主键为 4,而目标是主键 6 → 继续向右搜索。
  2. 遍历槽内记录:找到对应的槽组后,通过 next_record 依次遍历记录,直到匹配目标。

如果目标记录在最后一个槽组中,则需先定位前一个槽位的末尾记录,再向后查找。

这种设计结合链表的灵活性与目录的高效定位,是一种典型的“空间换时间”方案。


File Header(页头信息)

每个页的开头都有一个 File Header 区域,包含以下关键字段:

  • FIL_PAGE_OFFSET:当前页的页号(唯一标识)
  • FIL_PAGE_PREV / FIL_PAGE_NEXT:当前页的前后页号,用于双向链表连接页

这些字段将所有页串联成一个链式结构,实现页之间的双向导航。

InnoDB 和 MyISAM对比

InnoDB 与 MyISAM 核心区别对比表

对比项

InnoDB

MyISAM

事务支持

支持,符合 ACID

不支持

锁机制

行级锁(高并发)

表级锁(写操作会阻塞整个表)

外键支持

支持外键约束

不支持

崩溃恢复

有 redo log 自动恢复机制

崩溃后可能丢失数据,需手动修复

存储文件

.ibd

(数据 + 索引)

.MYD

(数据)+ .MYI

(索引)

索引类型

聚簇索引(主键排序存储)

非聚簇索引(索引与数据分离)

全文索引

5.6 及以上版本支持

原生支持,性能优

COUNT(*)

需扫描全表,性能较慢

直接读取行数,性能极高

压缩功能

不支持表压缩

支持表压缩,适合节省存储空间

典型场景

高并发事务场景,如订单、支付系统

读多写少的场景,如日志、分析型查询


核心差异详解
(1)事务与锁机制
  • InnoDB:支持事务,采用行级锁,并发处理能力强,适合写操作频繁的业务。
  • MyISAM:不支持事务,采用表级锁,在写入时会锁整个表,效率低,易产生阻塞。
(2)索引与存储设计
  • InnoDB 使用 聚簇索引,数据按主键物理排序,主键查询效率极高;但二级索引需通过主键“回表”访问数据。
  • MyISAM 的索引和数据存储是分离的,索引中保存数据地址,所有查询都需通过索引定位数据。
(3)性能权衡
  • MyISAM 在执行 COUNT(*)全文检索方面表现更优,但牺牲了事务一致性与并发性能。
  • InnoDB 提供了更强的安全性与并发控制,适合在线交易等对数据可靠性要求高的系统。
(4)故障恢复能力
  • InnoDB 使用 redo log(重做日志),在数据库异常崩溃后可自动恢复,保障数据一致性。
  • MyISAM 不具备崩溃自动恢复机制,数据损坏需手动执行 REPAIR TABLE 恢复。

总结建议
  • 选择 InnoDB:若业务需要事务支持、数据安全、高并发写入,如订单处理、支付系统等 OLTP 场景。
  • 选择 MyISAM:适用于只读或写入极少的场景,如静态日志查询、临时数据分析。但在 MySQL 8.0+ 版本中,MyISAM 已不再推荐使用,官方也逐步弱化其支持。

http://www.dtcms.com/a/313576.html

相关文章:

  • C++ : 反向迭代器的模拟实现
  • 【图像处理基石】如何使用deepseek进行图像质量的分析?
  • vllm0.8.5:思维链(Chain-of-Thought, CoT)微调模型的输出结果包括</think>,提供一种关闭思考过程的方法
  • MCP协议:CAD地图应用的AI智能化解决方案(唯杰地图MCP)
  • 【数据结构与算法】数据结构初阶:排序内容加餐(二)——文件归并排序思路详解(附代码实现)
  • 【C++】面向对象编程
  • C语言(长期更新)第8讲 函数递归
  • 网络通信与Socket套接字详解
  • C#模式匹配用法与总结
  • 网页 URL 转 Markdown API 接口
  • 大模型中的Token和Tokenizer:核心概念解析
  • 【Unity3D实例-功能-镜头】俯视角
  • MySQL极简安装挑战
  • 数据结构代码
  • IO流-数据流
  • 语义分割--deeplabV3+
  • 企业级AI Agent构建实践:从理论到落地的完整指南
  • 机器学习中的经典算法
  • 算法讲解--最大连续1的个数
  • C++异常与智能指针,资源泄露
  • CMake 命令行参数完全指南
  • 【动态规划算法】路径问题
  • kubernetes基础知识
  • Linux命令基础(下)
  • Day22--回溯--77. 组合,216. 组合总和 III,17. 电话号码的字母组合
  • 深入剖析Java拦截器:从原理到实战
  • Python3 中使用zipfile进行文件(夹)的压缩、解压缩
  • 一加Ace5无法连接ColorOS助手解决(安卓设备ADB模式无法连接)
  • 跟我学C++中级篇——常函数
  • javaweb开发之Servlet笔记