MySQL 底层执行原理:输入SQL语句到两阶段提交
文章标题
- [MySQL 的整体架构](#MySQL 的整体架构)
- [核心重点 1:一条 Query(查询)语句的漫游记](#核心重点 1:一条 Query(查询)语句的漫游记)
- [核心重点 2:一条 Update(更新)语句与"两阶段提交"](#核心重点 2:一条 Update(更新)语句与“两阶段提交”)
- 两阶段提交的物理全过程
MySQL 的整体架构
大体来说,MySQL 服务端可以分为 Server 层和存储引擎层两部分。
-
Server 层(通用功能层):包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能。所有的内置函数(如日期、时间、数学和加密函数等),以及跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。它有自己的专属日志系统:binlog(归档日志)。
-
存储引擎层(数据读写层):负责数据的存储和检索。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。InnoDB 拥有独有的日志系统:redo log(重做日志)和 undo log(撤销日志)。
核心重点 1:一条 Query(查询)语句的漫游记
当一条 SELECT 语句从客户端发出后,在 Server 层和引擎层之间的执行流程如下:

1、 连接器:连接器负责跟客户端建立连接、获取权限、维持和管理连接 。
- 身份认证:在完成经典的 TCP 握手后,连接器就要开始认证你的身份 。如果用户名或密码不对,你就会收到一个 Access denied for user 的错误 。
- 权限读取:认证通过后,连接器会到系统权限表里面查出你拥有的权限 。注意: 之后这个连接里面的权限判断逻辑,都将依赖于此时读到的权限 。这意味着,即使管理员中途修改了权限,只要你的连接不断开,权限就不会变。
2、查询缓存
在 MySQL 5.7 及以下版本,连接完成后会先到查询缓存看看,之前是不是执行过这条语句 。之前执行过的语句及其结果可能会以 key-value 对的形式被直接缓存在内存中(key 是查询语句,value 是查询结果) 。如果命中,直接返回,效率极高 。
3、分析器 - 词法分析(背单词):MySQL 从你输入的 "select" 识别出来这是一个查询语句 。
它也要把字符串 "user" 识别成"表名 user",把字符串 "id" 识别成"列 id" ,主要负责从 SQL 语句中提取关键字、表名、字段名、查询条件等 。 - 语法分析(组句子):根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法 。如果 SQL 语句不对,就会返回 You have an error in your SQL syntax 的错误提醒 。
4、优化器
经过分析器后,语句正确,就会进入优化器 。优化器的作用是在基于同一个查询语句的多个查询方案中找出效率最高的。
5、执行器:调用接口返回结果
核心重点 2:一条 Update(更新)语句与"两阶段提交"
** 更新流程涉及到了两个极为核心的日志模块:redo log(重做日志)和 binlog(归档日志) 。 **
1、为什么不能直接写磁盘?
如果每一次更新操作都需要直接写进磁盘,磁盘需要先在密密麻麻的数据页里找到对应的那条记录,然后再更新,整个过程的 IO 成本、查找成本都极高 。
为了解决这个问题,MySQL 引入了 WAL(预写式日志)技术。它的关键点就是先写日志,再写磁盘 。

两阶段提交的物理全过程
由于 redo log 和 binlog 是两个独立的日志系统,如果不用特殊的机制约束,它们之间就会出现逻辑不一致的问题 。
普通提交:
- 先写 redo log,后写 binlog:如果写完 redo log 后系统 Crash。重启后利用 redo log 恢复,原库这一行是 1;但由于 binlog 没写进去,未来主从同步或者用 binlog 恢复临时库时,少了这个事务,新库这一行是 0。主从数据不一致!
- 先写 binlog,后写 redo log:如果写完 binlog 后系统 Crash。重启后由于 redo log 没写,原库该事务无效,这一行是 0;但 binlog 已经记录了修改,未来主从同步或用 binlog 恢复临时库时,新库多出了这个事务,这一行变成 1。主从数据又不一致!
内部两阶段提交机制:
1、寻行:执行器先找引擎取 ID=2 这一行 。引擎用树搜索找到该行,如果在内存中就直接返回给执行器,否则先从磁盘读入内存再返回 。
2、计算:执行器拿到行数据,把 c 的值加上 1(得到新的一行数据),再调用引擎接口写入这行新数据 。
3、【阶段一】Prepare:引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态 。然后告知执行器执行完成了,随时可以提交事务 。
4、【阶段二】写 binlog:执行器生成这个操作的 binlog,并把 binlog 写入磁盘 。
5、【阶段三】Commit:执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新正式完成 。