MySQL 架构分层与请求全过程
面试里问“MySQL 有几层”,不是让你背一个固定数字。真正要回答清楚的是:客户端发来一条 SQL 后,请求经过哪些模块,每一层解决什么问题,binlog、redo log、undo log 分别在哪里,慢 SQL、连接满、锁等待、刷盘慢分别该看哪一层。
这一页把 MySQL 从外到内拆开,再用 select 和 update 两条链路把所有层串起来。
这一页解决这些问题
| 问题 | 要掌握到什么程度 |
|---|---|
| MySQL 到底有几层 | 能说三层,也能展开五层,不死背数字 |
| 每层负责什么 | 连接、解析、优化、执行、引擎、页、日志边界清楚 |
| binlog、redo、undo 在哪 | binlog 属于 Server 层,redo/undo 属于 InnoDB |
| 一条 select 怎么执行 | 从连接、解析、优化器到 InnoDB B+Tree 读页 |
| 一条 update 怎么提交 | 从加锁、undo、脏页、redo、binlog 到两阶段提交 |
| 线上问题怎么定位 | 能按层判断连接满、慢 SQL、锁等待、IO 慢、主从延迟 |
不要死背“几层”
不同资料对 MySQL 架构分层叫法不完全一样。学习和面试时建议掌握两种表达。
| 表达方式 | 分层 | 适合场景 |
|---|---|---|
| 三层模型 | 连接层、Server 层、存储引擎层 | 面试快速回答 |
| 五层模型 | 客户端层、连接层、SQL 层、存储引擎层、文件与日志层 | 深入解释流程和排查 |
三层模型强调职责边界:
mermaid
flowchart TD
A["连接层<br/>连接、认证、权限、线程"] --> B["Server 层<br/>解析、预处理、优化器、执行器、binlog"]
B --> C["存储引擎层<br/>InnoDB、B+Tree、Buffer Pool、锁、MVCC、redo、undo"]
C --> D["磁盘文件<br/>表空间、数据页、索引页、日志文件"]五层模型强调一次请求经过的完整路径:
mermaid
flowchart TD
A["客户端层<br/>JDBC、连接池、ORM"] --> B["连接层<br/>TCP、认证、权限、连接状态"]
B --> C["SQL 层<br/>解析、预处理、优化、执行"]
C --> D["存储引擎层<br/>InnoDB 接口、索引、事务、锁"]
D --> E["文件与日志层<br/>数据页、undo、redo、binlog 文件"]这两个说法并不矛盾。三层适合快速讲边界,五层适合讲全过程。
每层到底负责什么
| 层 | 负责什么 | 典型组件 | 不负责什么 |
|---|---|---|---|
| 客户端层 | 发起连接、发送 SQL、管理连接池 | JDBC、MyBatis、HikariCP、Navicat | 不负责真正执行 SQL |
| 连接层 | 建连接、认证、权限、连接状态 | 连接器、线程、权限检查 | 不负责选择索引 |
| SQL 层 | 理解 SQL、选择执行计划、调度执行 | 解析器、预处理器、优化器、执行器、binlog | 不直接管理 InnoDB 数据页 |
| 存储引擎层 | 读写数据页、维护索引、事务、锁、MVCC | InnoDB、Buffer Pool、B+Tree、redo、undo | 不解析 SQL 文本 |
| 文件与日志层 | 数据和日志落盘 | ibd、redo log file、undo tablespace、binlog file | 不决定业务语义 |
一个容易混淆的边界:
| 机制 | 所属层 | 主要作用 |
|---|---|---|
| binlog | Server 层 | 主从复制、时间点恢复、审计回放 |
| redo log | InnoDB 引擎层 | 崩溃恢复,保证已提交事务不丢 |
| undo log | InnoDB 引擎层 | 事务回滚,MVCC 历史版本 |
| Buffer Pool | InnoDB 引擎层 | 缓存数据页和索引页 |
| 优化器 | Server 层 | 根据统计信息选择访问路径 |
select 请求全过程
一条查询 SQL 不会直接去磁盘找数据,而是先经过 Server 层生成执行计划,再由执行器调用 InnoDB。
mermaid
flowchart TD
A["客户端发送 SELECT"] --> B["连接层检查连接和权限"]
B --> C["解析器做词法和语法分析"]
C --> D["预处理器检查表、列、权限"]
D --> E["优化器选择索引和 Join 顺序"]
E --> F["执行器按计划调用引擎接口"]
F --> G["InnoDB 从 B+Tree 定位页"]
G --> H{"Buffer Pool 是否命中"}
H -- "命中" --> I["内存读取记录"]
H -- "未命中" --> J["从磁盘加载页"]
J --> I
I --> K["返回行给执行器"]
K --> L["Server 层过滤、排序、聚合"]
L --> M["返回结果给客户端"]每一步发生什么:
| 步骤 | 做什么 | 如果出问题会怎样 |
|---|---|---|
| 连接层 | 校验账号、权限、连接状态 | 连接失败、连接数满、权限不足 |
| 解析器 | 判断 SQL 语法 | 语法错误直接失败 |
| 预处理器 | 检查表字段是否存在 | 表不存在、字段不存在 |
| 优化器 | 选索引、Join 顺序、估算 rows | 统计信息不准可能选错索引 |
| 执行器 | 调用引擎,一行行取结果 | 扫描多、回表多会慢 |
| InnoDB | 查 B+Tree、读页、MVCC 判断 | IO 高、锁等待、版本链长 |
| Server 返回 | 排序、聚合、网络返回 | filesort、临时表、大结果集慢 |
update 请求全过程
update 比 select 多了事务、锁、undo、redo、binlog 和提交过程。
mermaid
flowchart TD
A["客户端发送 UPDATE"] --> B["连接层和 SQL 层处理"]
B --> C["优化器选择定位记录的索引"]
C --> D["执行器调用 InnoDB"]
D --> E["InnoDB 定位记录并加行锁"]
E --> F["生成 undo log 旧版本"]
F --> G["修改 Buffer Pool 中的数据页"]
G --> H["生成 redo log record"]
H --> I["提交阶段 redo prepare"]
I --> J["Server 层写 binlog"]
J --> K["redo commit"]
K --> L["返回提交成功"]这里最关键的是两阶段提交:
mermaid
sequenceDiagram
participant T as Transaction
participant R as InnoDB redo
participant B as Server binlog
T->>R: write redo prepare
T->>B: write binlog
T->>R: write redo commit
T-->>T: commit success为什么要这样做?
| 如果没有两阶段提交 | 可能后果 |
|---|---|
| redo 成功,binlog 失败 | 主库恢复有数据,从库没有,主从不一致 |
| binlog 成功,redo 失败 | 从库可能重放了,主库恢复后没有 |
| 两者无法对齐 | 崩溃恢复时不知道事务到底该提交还是回滚 |
所以 update 链路里有两个层面的“正确”:
- InnoDB 用 undo、锁、redo 保证事务自身正确。
- Server 层 binlog 和 InnoDB redo 用两阶段提交保证复制和恢复一致。
为什么 Server 层和存储引擎层要分开
如果 MySQL 只有一个混在一起的巨大模块,会很难支持不同存储方式。分层以后:
| 好处 | 解释 |
|---|---|
| SQL 处理复用 | 解析器、优化器、执行器可以服务不同引擎 |
| 引擎可替换 | InnoDB、MyISAM、Memory 可以有不同存储实现 |
| 职责清晰 | Server 层管 SQL 语义,引擎层管数据页和事务 |
| 便于扩展 | binlog、复制、权限、优化器在通用层处理 |
但这种分层也带来一个问题:Server 层 binlog 和 InnoDB redo 是两套日志。所以事务提交必须通过两阶段提交协调它们。
按层排查线上问题
线上 MySQL 问题不要一上来就“加索引”或“重启”。先判断是哪一层。
mermaid
flowchart TD
A["MySQL 问题"] --> B{"表现是什么"}
B -- "连接失败或等待" --> C["查连接层"]
B -- "SQL 慢" --> D["查优化器和执行计划"]
B -- "锁等待或死锁" --> E["查 InnoDB 锁和事务"]
B -- "IO 高或刷盘慢" --> F["查 Buffer Pool、redo、磁盘"]
B -- "主从延迟" --> G["查 binlog、复制线程、大事务"]| 现象 | 优先看哪层 | 常用手段 |
|---|---|---|
| Too many connections | 连接层 | show processlist、连接池配置、max_connections |
| SQL 扫描很多行 | SQL 层、优化器 | EXPLAIN、慢日志、统计信息 |
| 明明有索引却不用 | 优化器 | show index、analyze table、直方图 |
| update 卡住 | InnoDB 锁 | show engine innodb status、事务表、锁等待 |
| 普通 select 很慢 | InnoDB、Buffer Pool | 命中率、IO、undo 版本链、返回行数 |
| commit 慢 | 日志和磁盘 | redo/binlog 刷盘策略、磁盘延迟 |
| 从库延迟 | binlog 和复制层 | 大事务、SQL 线程、并行复制、从库慢查询 |
可运行 Demo:观察连接、执行计划和引擎状态
准备表:
sql
create table layer_demo_order (
id bigint primary key auto_increment,
user_id bigint not null,
status tinyint not null,
amount decimal(10, 2) not null,
created_at datetime not null,
key idx_user_created (user_id, created_at),
key idx_status_created (status, created_at)
) engine = InnoDB default charset = utf8mb4;插入数据:
sql
insert into layer_demo_order(user_id, status, amount, created_at)
values
(1001, 1, 99.00, now()),
(1001, 2, 199.00, now()),
(1002, 1, 299.00, now());观察连接层:
sql
show processlist;
show global status like 'Threads%';
show variables like 'max_connections';观察优化器计划:
sql
explain
select id, amount
from layer_demo_order
where user_id = 1001
order by created_at desc
limit 10;观察 InnoDB 状态:
sql
show engine innodb status\G更新统计信息:
sql
analyze table layer_demo_order;这个 Demo 对应的学习重点是:同一个 SQL 问题,可能涉及连接层、优化器、InnoDB、日志和磁盘,不要只盯一个点。
常见误区
| 误区 | 为什么错 | 正确理解 |
|---|---|---|
| MySQL 架构只有一种官方标准层数 | 学习资料按不同角度划分 | 重点是职责边界,不是死背数字 |
| binlog 和 redo log 都是事务日志,所以一样 | 所属层、格式、用途不同 | binlog 管复制和恢复,redo 管崩溃恢复 |
| SQL 层会直接读磁盘 | SQL 层通过引擎接口访问数据 | InnoDB 管页、索引、Buffer Pool |
| 有索引就一定快 | 优化器可能不用,或回表太多 | 看 EXPLAIN、rows、filtered、Extra |
| update 只是改一行 | 涉及锁、undo、redo、binlog、刷盘 | 写链路比读链路复杂得多 |
面试标准回答
text
MySQL 可以按三层理解:连接层、Server 层和存储引擎层。连接层负责客户端连接、认证、权限和连接状态;Server 层负责 SQL 解析、预处理、优化器、执行器以及 binlog;存储引擎层负责真正的数据读写,例如 InnoDB 的 B+Tree、Buffer Pool、事务、锁、MVCC、redo log 和 undo log。
如果展开,也可以说成客户端层、连接层、SQL 层、存储引擎层、文件与日志层。select 请求会先经过连接和权限校验,再经过解析器、预处理器、优化器生成执行计划,执行器调用 InnoDB 接口,InnoDB 通过 B+Tree 和 Buffer Pool 读取数据页,最后返回结果。update 请求还会涉及行锁、undo log、修改 Buffer Pool、redo log、binlog 和两阶段提交。
binlog 属于 Server 层,主要用于主从复制和时间点恢复;redo log 和 undo log 属于 InnoDB,redo 用于崩溃恢复,undo 用于回滚和 MVCC。按层理解 MySQL,排查问题时才能区分连接满、优化器选错计划、锁等待、磁盘 IO 和主从延迟分别应该看哪里。关联知识点
| 知识点 | 说明 |
|---|---|
| MySQL 架构分层 | 基础分层说明 |
| SQL 执行全过程原理 | 查询 SQL 从连接到 InnoDB 的完整链路 |
| InnoDB 更新事务全过程 | update 的锁、undo、redo、binlog、MVCC |
| EXPLAIN 执行计划 | 优化器如何选择计划,rows 从哪里来 |
| redo log 与 binlog | 两阶段提交和崩溃恢复 |
