MySQL 架构分层
问“MySQL 有几层”,面试和学习里通常有两种回答方式:
- 简化三层架构:连接层、Server 层、存储引擎层。
- 展开五层架构:客户端层、连接层、SQL 层、存储引擎层、文件与日志层。
更推荐这样记:
MySQL 对外看是三层:连接层负责连接和权限,Server 层负责 SQL 解析、优化和执行调度,存储引擎层负责真正的数据读写。再往下展开,InnoDB 会依赖 Buffer Pool、数据页、redo log、undo log、binlog 协调等机制保证性能、事务和恢复。
学习目标
| 目标 | 需要掌握什么 |
|---|---|
| 知道分层 | 能说清连接层、Server 层、存储引擎层分别做什么 |
| 知道边界 | 知道 binlog 属于 Server 层,redo/undo 属于 InnoDB |
| 知道流程 | 能讲清一条 select、update 从客户端到 InnoDB 的链路 |
| 知道为什么 | 知道为什么 MySQL 要把 SQL 层和存储引擎层分开 |
| 会排查 | 慢 SQL、连接满、锁等待、磁盘慢分别可能在哪一层 |
| 会面试 | 能用清晰分层回答“MySQL 架构”“SQL 执行流程” |
简化三层架构
最常见的学习模型是三层:
flowchart TD
A["连接层<br/>连接、认证、权限、线程"] --> B["Server 层<br/>解析、预处理、优化、执行"]
B --> C["存储引擎层<br/>InnoDB、MyISAM、Memory"]
C --> D["磁盘文件和日志<br/>数据页、索引页、redo、undo"]| 层 | 负责什么 | 常见组件 |
|---|---|---|
| 连接层 | 建立连接、认证账号、权限校验、连接管理 | 连接器、线程、权限表 |
| Server 层 | 处理 SQL 语义,生成执行计划,调用引擎接口 | 解析器、预处理器、优化器、执行器、binlog |
| 存储引擎层 | 真正读写数据、维护索引、处理事务和锁 | InnoDB、Buffer Pool、B+Tree、redo、undo |
为什么这样分层:Server 层不用关心数据到底存在 InnoDB 还是 MyISAM;存储引擎层也不用关心 SQL 文本怎么解析。这样 MySQL 可以支持多个存储引擎。
展开五层架构
如果面试官追问“再细一点”,可以展开成五层:
flowchart TD
A["客户端层<br/>JDBC、Navicat、MyBatis"] --> B["连接层<br/>TCP、认证、权限、连接池"]
B --> C["SQL 层<br/>解析、预处理、优化、执行"]
C --> D["存储引擎层<br/>InnoDB 引擎接口"]
D --> E["文件与日志层<br/>表空间、数据页、redo、undo"]| 层级 | 说明 | 重点 |
|---|---|---|
| 客户端层 | 发起 SQL 请求 | JDBC、连接池、ORM、命令行客户端 |
| 连接层 | 管理连接和权限 | 连接数、认证、权限、空闲超时 |
| SQL 层 | 理解并执行 SQL | 解析器、优化器、执行器、binlog |
| 存储引擎层 | 访问数据和索引 | InnoDB、锁、MVCC、Buffer Pool |
| 文件与日志层 | 数据落盘和恢复 | 表空间、数据页、redo log、undo log |
这五层不是官方唯一叫法,但非常适合学习和面试表达。
第一层:客户端层
客户端层不是 MySQL Server 内部的一部分,但它决定 SQL 怎么发到 MySQL。
常见客户端:
| 客户端 | 示例 |
|---|---|
| Java 程序 | JDBC、MyBatis、JPA、HikariCP |
| 管理工具 | Navicat、DataGrip、MySQL Workbench |
| 命令行 | mysql -h127.0.0.1 -uroot -p |
| 中间件 | ShardingSphere、MyCat、ProxySQL |
常见问题:
| 现象 | 可能原因 |
|---|---|
| 应用连接数据库慢 | 连接池配置小、网络慢、数据库连接数满 |
| 偶发 timeout | SQL 慢、连接池等待、网络抖动 |
| 大量短连接 | 没用连接池或连接池配置错误 |
| 事务没提交 | ORM 或代码没有正确管理事务 |
所以排查 MySQL 问题时,不要只看数据库,也要看应用连接池、超时配置和 ORM 生成的 SQL。
第二层:连接层
连接层负责处理客户端连接。
flowchart TD
A["客户端发起 TCP 连接"] --> B["MySQL 接收连接"]
B --> C["账号密码认证"]
C --> D["权限加载和校验"]
D --> E["分配连接线程或线程资源"]
E --> F["等待客户端发送 SQL"]连接层主要做:
| 能力 | 说明 |
|---|---|
| 连接建立 | 基于 TCP 建立客户端和 MySQL Server 的连接 |
| 用户认证 | 校验用户名、密码、主机来源 |
| 权限校验 | 判断是否有库、表、列、操作权限 |
| 连接管理 | 维护连接状态、空闲时间、当前数据库、事务状态 |
| 线程处理 | 每个连接对应服务线程或线程池资源 |
常见参数:
| 参数 | 作用 |
|---|---|
max_connections | 最大连接数 |
wait_timeout | 非交互连接空闲超时时间 |
interactive_timeout | 交互连接空闲超时时间 |
max_user_connections | 单用户最大连接数 |
查看连接:
show processlist;
show global status like 'Threads%';
show variables like 'max_connections';如果 Threads_connected 很高,应用可能连接泄漏、连接池过大,或者 SQL 慢导致连接释放不及时。
第三层:SQL 层,也叫 Server 层
Server 层是 MySQL 的通用 SQL 处理层。大多数 EXPLAIN、优化器、binlog 都属于这一层。
flowchart TD
A["SQL 文本"] --> B["解析器"]
B --> C["预处理器"]
C --> D["优化器"]
D --> E["执行器"]
E --> F["调用存储引擎接口"]解析器
解析器负责把 SQL 文本变成 MySQL 能理解的结构。
它做两件事:
| 步骤 | 说明 |
|---|---|
| 词法分析 | 把字符串拆成关键字、表名、列名、常量、操作符 |
| 语法分析 | 判断 SQL 是否符合 MySQL 语法 |
例如:
select id, username from users where id = 1;解析器会识别出:
select 列表:id, username
from 表:users
where 条件:id = 1如果 SQL 写错,比如:
select from users;错误通常发生在解析器阶段。
预处理器
预处理器负责进一步检查语义:
| 检查 | 示例 |
|---|---|
| 表是否存在 | users 表是否存在 |
| 字段是否存在 | username 字段是否存在 |
| 字段是否有歧义 | 多表都有 id 时是否明确指定 |
| 权限是否满足 | 当前用户是否能查这张表 |
语法正确不代表语义正确。比如字段不存在,语法层面可能能解析,但预处理阶段会报错。
优化器
优化器负责决定“怎么执行更便宜”。
它会考虑:
| 优化点 | 说明 |
|---|---|
| 选择哪个索引 | idx_user_id 还是 idx_status_time |
| 表连接顺序 | 多表 join 谁先访问 |
| 访问方式 | const、ref、range、ALL |
| 是否走覆盖索引 | 查询字段是否都在索引里 |
| 是否需要排序 | 能否利用索引顺序 |
| 估算扫描行数 | 根据统计信息估算 rows |
优化器依赖统计信息,所以它可能选错索引。详细看 EXPLAIN 执行计划。
执行器
执行器负责按优化器选出的计划真正执行。
它会:
- 检查执行权限。
- 打开表。
- 调用存储引擎接口读取第一行。
- 判断 WHERE 条件是否满足。
- 满足就返回给客户端。
- 继续调用引擎读取下一行,直到结束。
执行器本身不直接理解 InnoDB 页结构,它通过统一的存储引擎接口和 InnoDB 交互。
binlog 属于 Server 层
binlog 是 MySQL Server 层的日志,不属于 InnoDB。
| 日志 | 所属层 | 作用 |
|---|---|---|
| binlog | Server 层 | 主从复制、增量恢复、审计回放 |
| redo log | InnoDB 层 | 崩溃恢复,保证已提交事务不丢 |
| undo log | InnoDB 层 | 回滚和 MVCC 旧版本 |
这也是为什么 redo log 和 binlog 要通过两阶段提交保证一致。详细看 redo log 与 binlog。
第四层:存储引擎层
存储引擎层负责真正的数据读写。MySQL 支持多种存储引擎,但商业项目里最常用的是 InnoDB。
查看支持的引擎:
show engines;常见引擎:
| 引擎 | 特点 | 是否常用 |
|---|---|---|
| InnoDB | 支持事务、行锁、MVCC、崩溃恢复 | 最常用 |
| MyISAM | 不支持事务,表锁为主 | 老系统可能有 |
| Memory | 数据在内存,重启丢失 | 特殊临时场景 |
InnoDB 主要负责:
| 能力 | 说明 |
|---|---|
| B+Tree 索引 | 聚簇索引、二级索引、范围扫描 |
| Buffer Pool | 缓存数据页和索引页 |
| 行锁 | 锁索引记录,不是简单锁物理行 |
| MVCC | 通过 undo log 和 Read View 支持快照读 |
| redo log | 崩溃恢复 |
| undo log | 回滚和历史版本 |
| change buffer | 优化部分二级索引写入 |
| purge | 清理不再需要的历史版本 |
第五层:文件与日志层
InnoDB 最终要把数据和日志落到磁盘。
flowchart TD
A["InnoDB 内存结构"] --> B["Buffer Pool"]
A --> C["Log Buffer"]
B --> D["数据文件和索引页"]
C --> E["redo log 文件"]
A --> F["undo 表空间或回滚段"]常见文件和日志:
| 内容 | 说明 |
|---|---|
| 表空间文件 | 保存 InnoDB 表和索引数据 |
| 数据页 | InnoDB 默认以 16KB 页为基本读写单位 |
| redo log | 记录页修改,用于崩溃恢复 |
| undo log | 保存旧版本,用于回滚和 MVCC |
| binlog | Server 层日志,用于复制和恢复 |
| relay log | 从库接收主库 binlog 后保存的中继日志 |
| slow log | 慢 SQL 日志,用于性能排查 |
很多性能问题都和这一层有关:
| 问题 | 可能原因 |
|---|---|
| 磁盘 IO 高 | Buffer Pool 命中率低、大量随机读、刷脏页 |
| 提交慢 | redo flush、binlog sync、磁盘 fsync 慢 |
| undo 膨胀 | 长事务导致历史版本无法清理 |
| 主从延迟 | binlog 产生快,从库回放慢 |
SELECT 查询经过几层
flowchart TD
A["客户端发送 SELECT"] --> B["连接层校验连接和权限"]
B --> C["解析器解析 SQL"]
C --> D["预处理器检查表和列"]
D --> E["优化器选择索引和执行计划"]
E --> F["执行器调用 InnoDB"]
F --> G["InnoDB 查 B+Tree"]
G --> H{"Buffer Pool 是否有页"}
H -- "有" --> I["直接读内存页"]
H -- "没有" --> J["从磁盘读页到 Buffer Pool"]
I --> K["返回行给执行器"]
J --> K
K --> L["执行器过滤、排序、返回客户端"]关键理解:
- SQL 语法处理在 Server 层。
- 索引页、数据页读取在 InnoDB 层。
- 是否走索引由优化器决定。
- 真正读取页的是存储引擎。
- 返回结果前可能还要在 Server 层过滤、排序、分组。
UPDATE 更新经过几层
flowchart TD
A["客户端发送 UPDATE"] --> B["连接层校验"]
B --> C["Server 层解析和优化"]
C --> D["执行器调用 InnoDB 定位记录"]
D --> E["InnoDB 加锁"]
E --> F["写 undo log 旧版本"]
F --> G["修改 Buffer Pool 中的数据页"]
G --> H["写 redo log prepare"]
H --> I["Server 层写 binlog"]
I --> J["redo log commit"]
J --> K["事务提交成功"]这个流程能解释几个高频问题:
| 问题 | 解释 |
|---|---|
| 为什么更新要加锁 | 防止并发写同一记录导致数据错乱 |
| 为什么能回滚 | undo log 保存了修改前版本 |
| 为什么提交后宕机能恢复 | redo log 能重放已提交页修改 |
| 为什么主从复制靠 binlog | binlog 是 Server 层逻辑变更日志 |
| 为什么 redo 和 binlog 要两阶段提交 | 保证 InnoDB 恢复和主从复制看到同一事务结果 |
MySQL 8 查询缓存去哪了
老版本 MySQL 有 Query Cache,位于 Server 层。它会缓存完整 SQL 的查询结果。
但 MySQL 8 已经移除查询缓存。原因是:
- SQL 必须完全相同才容易命中。
- 表更新会导致相关缓存失效。
- 高并发下维护 Query Cache 会带来锁竞争。
- 商业项目更常用 Redis、本地缓存或应用层缓存。
所以现在学习 MySQL 架构时,不要再把 Query Cache 当作核心能力。
分层和排查怎么对应
| 现象 | 优先看哪层 | 排查方向 |
|---|---|---|
| 连接数满 | 连接层 | show processlist、连接池、max_connections |
| SQL 语法报错 | SQL 层解析器 | SQL 拼写、关键字、括号 |
| 字段不存在 | SQL 层预处理器 | 表结构、别名、字段名 |
| 有索引但不用 | SQL 层优化器 | 统计信息、选择性、EXPLAIN |
| 查询扫描很多行 | 优化器 + InnoDB | 索引设计、rows、回表 |
| 锁等待 | InnoDB | 行锁、间隙锁、事务未提交 |
| 普通查询读到旧版本 | InnoDB MVCC | Read View、隔离级别 |
| 提交慢 | 日志层 | redo flush、binlog sync、磁盘 IO |
| 主从延迟 | Server 日志层 + 从库执行 | binlog、relay log、回放线程 |
| 宕机恢复慢 | InnoDB 日志层 | redo checkpoint、脏页、事务量 |
商业场景:为什么慢 SQL 不能只看 SQL
假设订单列表查询慢:
select id, order_no, status, created_at
from orders
where user_id = 1001
and status = 1
order by created_at desc
limit 20;按分层分析:
| 层 | 可能问题 |
|---|---|
| 客户端层 | 连接池等待,接口线程被占满 |
| 连接层 | 数据库连接数接近上限 |
| SQL 层 | 优化器没选到合适索引 |
| 存储引擎层 | 索引不匹配,扫描大量页或大量回表 |
| 文件日志层 | 磁盘 IO 高,Buffer Pool 命中率低 |
这就是为什么排查慢 SQL 要结合:
- 应用耗时。
- 连接池等待。
EXPLAIN。- 慢日志。
- Buffer Pool 命中率。
- 磁盘 IO。
常见误区
| 误区 | 正确理解 |
|---|---|
| MySQL 只有一层,就是数据库 | MySQL 至少要区分连接层、Server 层、存储引擎层 |
| 优化器属于 InnoDB | 优化器属于 Server 层 |
| binlog 属于 InnoDB | binlog 属于 Server 层 |
| redo log 和 binlog 一样 | redo 是 InnoDB 崩溃恢复日志,binlog 是 Server 复制恢复日志 |
| SQL 执行就是直接读磁盘 | 大量读取先走 Buffer Pool,缺页才读磁盘 |
| InnoDB 行锁锁的是物理行 | InnoDB 行锁加在索引记录上 |
| Query Cache 还能优化 MySQL 8 | MySQL 8 已经移除 Query Cache |
面试回答模板
如果被问“MySQL 有几层”,可以这样答:
MySQL 通常可以按三层理解:连接层、Server 层和存储引擎层。连接层负责客户端连接、认证、权限和连接管理;Server 层负责 SQL 解析、预处理、优化器生成执行计划、执行器调用存储引擎,同时 binlog 也在 Server 层;存储引擎层负责真正的数据读写,最常用的是 InnoDB,它负责 B+Tree 索引、Buffer Pool、事务、锁、MVCC、redo log 和 undo log。再往下是文件和日志层,保存表空间、数据页、redo、undo 等。执行 select 时,SQL 先经过连接、解析、优化,再由执行器调用 InnoDB 查索引和数据页;执行 update 时还会涉及加锁、undo、redo、binlog 和两阶段提交。关联知识点
| 知识点 | 继续学习什么 |
|---|---|
| MySQL 总览 | MySQL 学习路线 |
| InnoDB 存储结构 | 页、行、B+Tree、Buffer Pool |
| 索引知识点 | 聚簇索引、二级索引、覆盖索引 |
| EXPLAIN | 优化器和执行计划 |
| 事务 | ACID、隔离级别 |
| 锁机制 | 行锁、间隙锁、临键锁 |
| MVCC | Read View、undo 版本链 |
| redo log 与 binlog | Server 层和 InnoDB 层日志协同 |
本章小结
MySQL 可以简化理解为三层:连接层、Server 层、存储引擎层;也可以展开为客户端层、连接层、SQL 层、存储引擎层、文件与日志层。连接层解决“谁连进来、有没有权限”,Server 层解决“SQL 怎么解析、怎么优化、怎么执行”,InnoDB 解决“数据怎么存、怎么查、怎么加锁、怎么恢复”。理解分层后,才能把慢 SQL、锁等待、连接满、主从延迟、崩溃恢复这些问题放到正确位置分析。
