一文读懂 MySQL 执行流程与核心原理
前言
在开发中,我们会经常与 MySQL 打交道。一条简单的 UPDATE accounts SET balance = balance - 100 WHERE user = 'A' 语句,我们在客户端轻轻一点就执行完了。但你有没有想过,这条 SQL 语句在 MySQL 内部究竟经历了怎样的旅程?为什么即使在服务器突然断电的极端情况下,已提交的事务依然能保证数据不丢失?为什么要有 redo log 和 binlog 两套日志系统?
接下来,我们解析从客户端发出请求到数据最终落盘的全过程,揭开数据库系统最精妙的设计。
1 MySQL整体架构
要理解 SQL 的执行过程,首先要搞清楚 MySQL 的体系结构。MySQL 整体可以分为三层:连接层、Server 层和存储引擎层。
1.1 三层架构
| 层级 | 组件 | 功能描述 |
| 连接层 | 连接器、连接池 | 处理客户端连接请求,进行用户认证和授权,管理和复用连接 |
| Server层 | SQL接口、解析器、优化器、执行器 | SQL解析、优化、执行,跨存储引擎的服务功能 |
| 存储引擎层 | InnoDB、MyISAM、Memory等 | 负责数据的实际存储和读取,支持插件式架构 |
Server 层是 MySQL 的核心中枢,涵盖连接器、查询缓存(MySQL 8.0 已移除)、分析器、优化器和执行器,以及所有的内置函数(如日期、时间、数学等),所有跨存储引擎的功能(如存储过程、触发器、视图)都在这一层实现。
存储引擎层负责数据的存储和提取,采用插件式的架构模式,最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为默认存储引擎。
1.2 核心组件速览

2 一条UPDATE语句的完整旅程
假设我们执行这样一条更新语句:
UPDATE accounts SET balance = balance - 100 WHERE user = 'A';
2.1 Step1:连接器——建立连接与权限校验
客户端输入 mysql -uroot -proot 命令后,连接器首先完成经典的 TCP 三次握手,与 MySQL Server 建立 TCP 连接。随后,连接器对请求进行权限校验:
-
如果用户名或密码错误,会收到 错误提示,客户端程序结束执行;
-
校验通过后,连接器会从权限表将当前用户的所有权限查询并缓存起来,权限缓存的生命周期一直持续到该连接关闭。
注:权限修改后,已存在的连接不会立即生效——新权限只对新建的连接生效。
此外,还需要注意长连接带来的内存问题。MySQL 在执行过程中临时使用的内存是管理在连接对象里的,这些资源直到连接断开才会释放。如果长连接累积过多,可能导致内存占用过大,甚至被系统 OOM Kill,建议定期断开长连接。
2.2 Step2:分析器——拆解 SQL,检查语法
连接建立后,SQL 语句被传递给分析器。分析器的工作分为两个阶段:
1. 词法分析:将 SQL 字符串拆解成一个个词元,识别出 SQL 的关键字(如 UPDATE、WHERE)、表名、字段名和值等信息。
2. 语法分析:根据语法规则检查 SQL 语句的结构是否正确。如果 SQL 写错了,比如把 WHERE 写成了 WHER,分析器就会报错并终止执行。
2.3 Step3:优化器——选择最优执行路径
优化器是 SQL 执行效率的关键。它的核心任务是:在众多可能的执行方案中,选择一条最优路径。
具体来说,优化器会考虑以下因素:
1. 是否使用索引,以及使用哪个索引
2. 多表关联的连接顺序
3. 根据表的统计信息估算各方案的执行代价
最终生成一个"执行计划",供后续的执行器使用。
2.4 Step4:执行器——调用存储引擎接口
执行器是整个流程的"操作手"。拿到优化器生成的执行计划后,执行器会循环调用存储引擎提供的接口来逐行处理数据:
1. 对于查询操作,执行器调用存储引擎的"读取下一行"接口,每次取一行数据,判断是否符合查询条件
2. 对于更新操作(如 UPDATE),执行器先找到符合条件的数据行,调用存储引擎的更新接口执行修改,然后将更新操作写入 redo log 和 binlog 中。
3 两阶段提交:数据一致性的“守护神”
3.1 为什么要两阶段提交?
在 MySQL 内部,事务的提交涉及两个独立的日志系统——redo log(InnoDB 层)和 binlog(Server 层),如果没有协调机制,可能会出现"半成功"的灾难场景。
| 场景 | 问题表现 | 后果 |
| Redo log 先写成功,Binlog 未写就宕机 | 主库重启后通过 Redo log 恢复数据,但 Binlog 缺失该事务 | 主从复制后从库少了这笔记录,主从不一致 |
| Binlog 先写成功,Redo log 未写就宕机 | Binlog 中有该事务记录,但主库 Redo log 未提交,崩溃恢复后事务无效 | 主库回滚但从库执行了该事务,从库凭空多出数据 |
两阶段提交就是为了解决这一问题而引入的。它将一个事务的提交过程拆分为 Prepare 阶段和 Commit 阶段,并用一个全局唯一的事务 ID将 redo log 和 binlog 拴在一起,确保两份日志要么都成功,要么都失败(事务要么全做,要么全不做)。
3.2 两阶段提交的完整过程

具体步骤:
Prepare阶段:
1. InnoDB 将此次修改写入 redo log
2. redo log 的状态被标记为 prepare
3. redo log 根据参数设置决定是否刷盘
Commit阶段:
1. Server 层将 XID 写入 binlog
2. binlog 根据参数决定是否刷盘
3. 调用 InnoDB 引擎的提交接口,将 redo log 的状态从 prepare 改为 commit
3.3 崩溃恢复机制
两阶段提交的精妙之处体现在崩溃恢复时。MySQL 在重启后,根据以下逻辑判断事务是否需要提交。
| Redo log 状态 | Binlog 状态 | 恢复行为 | 原因 |
| 有 commit 标识 | 有完整记录 | 提交 | 事务已完成提交 |
| 仅有完整 prepare | 有完整记录 | 提交 | Binlog 已写,可用于主从同步 |
| 仅有完整 prepare | 缺失/不完整 | 回滚 | Binlog 未写,该事务不应生效 |
崩溃恢复时的判断逻辑:如果 redo log 里面的事务已经有了 commit 标识,直接提交;如果只有完整的 prepare,则进一步判断对应的事务 binlog 是否存在且完整——若完整则提交,否则回滚。
这个设计确保了无论崩溃发生在两阶段提交的哪个环节,最终两份日志的状态都是一致的。在时刻 A(redo log prepare 后、写入 binlog 之前)崩溃,发现 redo log 没有 commit,事务回滚;在时刻 B(写入 binlog 后、redo log commit 之前)崩溃,发现 binlog 完整,事务正常提交。
总结
回到开头的问题:为什么断电后已提交的事务不会丢失?因为 Redo log 在事务提交时已经写入了磁盘。为什么主从不会出现不一致?因为两阶段提交确保了 Redo log 和 Binlog 在逻辑上"荣辱与共"。
更多推荐



所有评论(0)