1. MySQL整体架构概览
MySQL 整体来说采用的是客户端-服务器(C/S)架构。对其进行详细分层的话,可以划分为客户端层 、服务层 和存储引擎层三大核心部分,每一层负责不同的功能,共同完成从接收用户请求到数据存储和查询的全过程:
- 客户端层:负责与用户或应用程序交互,接收 SQL 请求并返回结果
- 服务层 :核心处理层,负责解析、优化、执行 SQL 语句,并管理连接、权限、缓存等 -存储引擎层:负责实际的数据存储和读取,支持多种存储引擎(如 InnoDB、MyISAM 等)
此外,还有一些跨层的功能模块,如连接管理、日志系统和事务管理等
bash
客户端(应用程序/JDBC/ODBC/golang的数据库通用接口包database/sql包)
↓↑(通过网络或本地连接)
连接层(连接管理、权限验证)
↓↑
服务层(解析器、优化器、执行器、查询缓存)
↓↑
存储引擎层(InnoDB、MyISAM、Memory等)
↓↑
物理存储(数据文件、日志文件)
1.1 客户端层
作用:客户端层是用户或应用程序与 MySQL 交互的入口,支持多种编程语言和工具(如Navicat、Sequel Pro)
功能:
- 发起 SQL 查询或管理命令(如
SELECT
、INSERT
、CREATE TABLE
) - 通过网络协议(如 TCP/IP(可通过SSL/TLS进行加密)、Unix 套接字、Windows中的命名管道及共享内存、SSL/TLS)与 MySQL 服务器建立连接
- 接收服务器返回的结果集或错误信息
特点:
- 客户端可以是命令行工具、GUI 工具或应用程序代码
- 客户端与服务器之间的通信通常基于 MySQL 的专用协议(私有的二进制应用层协议MySQL Protocol ,基于 TCP/IP 传输)
1.2 服务层
服务层是 MySQL 的核心,包含了大部分逻辑处理功能
1.2.1 连接管理
作用:管理客户端与服务器之间的连接
功能:
- 接收客户端连接请求,建立通信通道
- 分配线程池(Thread Pool)或专用线程处理每个连接
- 进行用户身份认证(基于用户名、密码、主机地址等)
- 权限验证,确保用户有权执行请求的操作
1.2.2 解析器
作用:解析客户端发送的 SQL 语句,生成解析树
功能:
-
词法分析:将 SQL 语句拆分为词法单元(Token),如关键字、表名、列名等
-
语法分析:检查 SQL 语句的语法是否正确,确保符合 MySQL 的语法规则
-
语义分析:验证表、列、函数等是否存在,检查权限和上下文合法性
-
输出:生成一棵解析树,供后续优化器使用
sqlSELECT id, name FROM users WHERE age > 18;
在这个示例中,解析器会识别
SELECT
、FROM
、WHERE
等关键字,并验证users
表和age
列是否存在
1.2.3 优化器
作用:根据解析树生成高效的执行计划
功能:
- 分析 SQL 语句,评估多种可能的执行路径
- 根据统计信息(如表的大小、索引分布)选择代价最低的执行计划
- 优化操作包括选择最优的索引 、决定表连接顺序(对于多表查询) 、改写子查询或简化表达式
- 支持基于规则的优化(RBO)和基于成本的优化(CBO)
sql
SELECT * FROM users JOIN orders ON users.id = orders.user_id WHERE users.age > 18;
对于上述示例sql,优化器可能决定先过滤 users.age > 18
,再进行表连接。而如果 users.id
有索引,优化器可能选择基于索引的连接,我们可以使用 EXPLAIN
命令查看优化器的选择
1.2.4 执行器
作用:根据优化器生成的执行计划,调用存储引擎的接口执行查询
功能:
- 按执行计划的步骤,逐一调用存储引擎的 API(如读取行、扫描索引)
- 对于复杂查询,可能涉及临时表、排序或分组操作
- 将最终结果集返回给客户端
- 支持流式返回结果(减少内存占用)或缓存结果(用于排序等)。
需要注意的是, 执行器与存储引擎交互,但不直接操作底层数据文件
1.2.5 查询缓存(MySQL 8.0 前)
缓存 SELECT 查询的文本及其结果,以加速相同查询的响应
由于缓存对表数据的任何修改(如 INSERT、UPDATE)敏感,修改后缓存即失效(从5.7.20就已经开始不推荐查询缓存了),同时也存在性能问题和维护成本,MySQL 8.0 正式移除查询缓存(也是在oracle公司接管以后)。现在通常使用外部缓存如 Redis
1.2.6 其他服务层功能
事务管理:协调事务的开始、提交和回滚,依赖存储引擎实现 ACID 属性
日志管理:
- 二进制日志:记录所有修改操作,用于复制和恢复
- 慢查询日志:记录执行时间超阈值的查询,用于性能分析
- 通用日志:记录所有 SQL 操作,调试用
- 内置函数和存储过程:支持数学、字符串、日期等函数,以及用户定义的存储过程和触发器
1.3 存储引擎层
存储引擎层是 MySQL 的特色,负责实际的数据存储、索引管理和物理操作,采用插件式架构,向上面的服务层提供统一的接口(即存储引擎API),服务层对查询进行优化之后,只需要按照生成的执行计划,调用不同的存储引擎API即可。MySQL允许用户根据需求选择不同的存储引擎
存储引擎决定了数据的存储格式、索引结构、事务支持等。MySQL默认存储引擎为InnoDB,其特性主要包括:
- 支持事务(ACID 特性)
- 行级锁,适合高并发场景
- 支持外键和崩溃恢复
- 使用聚簇索引,数据按主键组织
其他的存储引擎特性如下:
MyISAM(MySQL从05年推出InnoDB存储引擎前的存储引擎):
- 不支持事务,表级锁
- 适合读多写少的场景(如数据仓库)
- 存储格式紧凑,占用空间小
Memory(不用磁盘来存储数据):
- 数据存储在内存中,速度快但断电丢失。
- 适合临时表或缓存。
1.3.1 存储引擎功能
- 数据存储 :将数据写入磁盘文件(如 InnoDB 的
.ibd
文件) - 索引管理:维护 B+ 树、哈希索引等结构
- 锁机制:实现不同粒度的锁(如 InnoDB 的行锁,MyISAM 的表锁)
- 事务支持:InnoDB 提供事务日志(Redo Log、Undo Log)实现事务一致性
- 物理优化:如 InnoDB 的缓冲池(Buffer Pool)缓存热点数据
1.3.2 与服务层的交互
- 服务层通过标准接口(如 Handler API)与存储引擎通信
- 存储引擎不解析 SQL,仅响应服务层的请求(如"读取某行""更新某记录")
- 不同存储引擎对同一 SQL 的执行效率可能差异很大
1.3.3 以InnoDB为例浅析页机制
- 以页(Page)为单位的磁盘 I/O
- 内存与磁盘之间的交互机制
InnoDB 将表中的数据持久化存储在磁盘上 ,即使数据库关机重启,数据仍然可以保留,但数据库的读写操作主要发生在内存中 ,因为磁盘 I/O 的速度比内存慢几个数量级,频繁地直接读写磁盘会严重影响性能。为了解决这个问题,InnoDB 引入了页机制:
- 页是磁盘和内存之间交互的基本单位(默认行格式为Dynamic)
- InnoDB 中的页大小通常为 16KB
- 不管你要读取一条记录,还是修改一列,最小的加载单位就是一个页
当你执行查询,InnoDB 的流程大致如下:
- 判断所需数据所在页是否已在内存中(Buffer Pool)
- ✅ 若已在内存:直接从 Buffer Pool 中读取,无需磁盘操作
- ❌ 若不在内存:从磁盘中将对应的 16KB 页 读入 Buffer Pool,再从中取出你需要的那一条或几条记录

所以不是"一条一条从磁盘中读记录",而是"一次读一页(16KB),一页中可能包含多条记录"
当你插入或更新数据时:
- InnoDB 先在内存中的 Buffer Pool 中修改对应页
- 同时写入 redo log (用于崩溃恢复)和 undo log(用于事务回滚)
- 后续通过后台线程将修改后的页 异步刷回磁盘 (即刷新脏页 )

页机制的优点有:
- 减少磁盘 I/O 次数:一次性读写 16KB,比频繁读写单条记录更高效
- 数据局部性原理:一页中的记录往往来自相邻主键或同一索引区间,命中概率高
- 更好地管理缓存:Buffer Pool 以页为单位组织,提高缓存命中率
Buffer Pool,即页的"内存缓存区":
- 用于缓存数据页(Data Page)、索引页(Index Page)、插入缓冲页等
- 加速读写,避免频繁访问磁盘
- 页被淘汰策略通常是 LRU(最近最少使用)
1.3.4 InnoDB聚簇索引查找
聚簇索引结构决定了数据记录在页内和页间的物理组织方式
在 每个数据页内部:
- InnoDB 会将所有记录通过 单向链表(实际上是一个页内偏移量的列表)连接起来
- 这些记录根据主键值大小排序
- 即使做了插入、删除、更新操作,这个链表始终保持主键有序
📌 特点:
- 记录间通过
next_record_offset
连接 - 页头有
PAGE_HEAP_TOP
(记录起始指针),页尾有PAGE_DIRECTION
(指向最大记录) - 插入新记录时,会修改相邻节点的指针,保持顺序
页与页之间的双向链表结构:
-
除了页内记录链表,每个页之间也有一个 双向链表:
- 当前页的
FIL_PAGE_PREV
指向前一页 - 当前页的
FIL_PAGE_NEXT
指向后一页
- 当前页的
📌 这个链表确保了你可以通过页头尾指针快速遍历整个 B+ 树的叶子层
这两种结合的设计带来的好处有 节省空间 + 维护顺序 + 支持范围查找
css
[Page 1] ---> [Page 2] ---> [Page 3] ← 页之间双向链表
Page 1 内部:
[最小记录] -> [记录1(pk=1)] -> [记录2(pk=5)] -> [最大记录] ← 记录单链表(按主键有序)
在 InnoDB 的聚簇索引中,无论如何增删改数据,页内记录总是通过一个主键有序的单链表连接起来,而数据页之间则通过双向链表连接,构成叶子节点层的可遍历结构。
而当 InnoDB 中的数据页上有记录被标记为删除 后,这些被删除的记录并不会立刻物理删除 (因为涉及事务回滚等机制),而是被链接成一个特殊的单向链表 ,称为 "空闲记录链表" (也俗称"垃圾链表"),链表的入口由页头的字段 PAGE_FREE
指向(记录偏移量),每条被删除的记录中有 next_record
字段,会指向下一个被删除的记录,这些"空间"将被未来插入的数据优先使用
1.3.5 InnoDB 数据页结构 + 聚簇索引查找路径的核心原理
✅ InnoDB 数据页结构中的定位机制全景总结:
🔹 数据页组成(7 个部分):
InnoDB 每个数据页(通常 16KB)由以下 7 个部分组成:
序号 | 区域名称 | 功能简述 |
---|---|---|
1 | File Header | 页的元信息(如页类型、页号) |
2 | Page Header | 页的状态(记录数量、目录指针等) |
3 | Infimum + Supremum | 虚拟最小、最大记录,用于页内链表的边界 |
4 | User Records | 用户插入的数据记录 |
5 | Free Space | 空闲可用空间(新记录写入位置) |
6 | Page Directory | 槽区 ,用于快速定位记录的索引(重点! ) |
7 | File Trailer | 校验信息(防止页损坏) |
🔹 记录链表:页内记录链结构(主键有序)
- 每个数据页内部的记录是通过主键顺序 组成的单链表
- 从
infimum
→ 第一条用户记录 → 第二条 → ... →supremum
- 无论插入/删除/更新,InnoDB 始终保持这个链表的主键有序性
🔹 页目录(Page Directory):快速查找关键
页目录位于数据页的末尾,是一个记录偏移量数组,用于加速查找
- 类似一个"快速跳转索引"
- 把页中的记录划分为若干组(槽 slot)
- 每个槽指向该组记录中"最大"的一条记录(即段尾)
- 页目录中偏移量是降序排列的
- 通过二分查找快速定位到所属槽,再在线性遍历
🔍 举个形象的查找过程例子
假设我们要查找主键 pk=27
,查找路径是:
- 根据 B+ 树的聚簇索引结构,定位到含有 pk=27 的数据页;
- 在该页中进入页目录部分,使用二分查找定位到"可能包含 pk=27 的槽";
- 顺着该槽指向的最大记录向前遍历主键有序的单链表
- 找到目标记录或确认不存在
总结:每个数据页中,InnoDB 使用主键有序单链表 组织记录,同时维护一个 页目录(记录指针数组) ,可以通过 二分查找快速锁定槽,再从槽内顺序扫描,提升页内查找性能
1.4 物理存储层
作用:存储实际的数据文件、索引文件和日志文件
主要文件类型:
- 数据文件 :如 InnoDB 的
.ibd
(每表独立表空间)或.frm
(表结构定义) - 索引文件:存储引擎维护的索引数据(如 InnoDB 索引与数据集成)
- 日志文件 :
- Redo Log:记录事务的修改操作,用于崩溃恢复
- Undo Log:记录事务的回滚信息,支持 MVCC(多版本并发控制)
- Binlog:记录逻辑修改,跨存储引擎,用于复制和恢复
存储位置 :由 MySQL 的 datadir
参数指定
1.5 sql执行过程
以下是一个典型 SELECT 查询的执行流程,展示各层如何协作:
- 客户端 :发送
SELECT id, name FROM users WHERE age > 18;
- 连接层:验证用户身份,建立连接,分配线程
- 解析器:解析 SQL,生成解析树,验证语法和语义
- 优化器 :分析查询,生成最优执行计划(如使用
age
索引) - 执行器:调用存储引擎接口,按计划执行查询
- 存储引擎:读取索引和数据,返回结果集
- 服务层:将结果集格式化,返回给客户端
