01.1 - MySQL 架构总览
定位:MySQL 架构的分层设计——为什么分 Server 层和存储引擎层?各组件如何协作? 面试高频度:⭐⭐⭐⭐⭐ 考查方式:"一条 SQL 的执行过程"是必考题,理解架构才能回答完整
一、这是什么?为什么需要它?
是什么
MySQL 采用 C/S 架构,核心是 Server 层 + 存储引擎层的双层体系。这种设计将"数据处理"和"数据存储"解耦——Server 层处理查询逻辑,存储引擎层管理实际的数据读写。
为什么需要分层?
没有分层会怎样? 想象一个单体的数据库:
- 每次事务提交都要和磁盘文件格式绑定
- 想要支持行锁就要重写整个查询处理器
- 想切换存储方式(比如内存表、列存)无法做到
分层的好处:
- 存储引擎可插拔 —— 不同场景用不同引擎,不影响上层 SQL 接口
- 关注点分离 —— Server 层专注查询优化/解析,引擎层专注数据读写
- 扩展性强 —— 第三方可以实现自己的引擎(如 RocksDB 作为 MyRocks)
核心 insight:分层是软件工程中"开闭原则"的典型应用——对扩展开放(新引擎),对修改封闭(上层 SQL 接口不变)。
二、原理拆解
2.1 连接层(Connectors)
用户线程 ─→ 连接器 ─→ 连接池 ─→ 线程复用
│
┌─────┴──────┐
│ 认证/SSL │
│ 权限检查 │
│ wait_timeout│
└────────────┘为什么需要连接池? 创建线程是昂贵的系统调用。MySQL 为每个客户端连接分配一个线程,通过线程池复用避免频繁创建/销毁的开销。
关键参数:
max_connections:最大连接数(默认 151)wait_timeout:非交互超时(默认 28800s)thread_cache_size:线程缓存大小
2.2 查询缓存(Query Cache,8.0 移除)
SELECT * FROM users WHERE id = 1
→ 查询缓存在 Hash Map 中查找 → 命中直接返回
→ 未命中 → 向下传递为什么被移除?
- 查询缓存在多核高并发下需要加锁保护
- 对写频繁的表,缓存失效太频繁,命中率极低
- 8.0 之前默认关闭,因为弊大于利
2.3 分析器(Parser)
SQL: SELECT * FROM users WHERE id = 1
↓
词法分析: 拆分为 token [SELECT, *, FROM, users, WHERE, id, =, 1]
↓
语法分析: 构建语法树
┌───────────┐
│ SELECT │
│ ├─ * │
│ ├─ FROM │
│ │ └─users│
│ └─ WHERE │
│ └─id=1 │
└───────────┘
↓
语义检查: users 表存在吗?id 列存在吗?有权限吗?关键字 SELECT 和标识符 users 的处理路径完全不同——这是分析器的职责。
2.4 优化器(Optimizer)
语法树
↓
┌───────────────────────┐
│ 优化器 │
│ │
│ 1. 选择索引 │
│ 2. 决定 JOIN 顺序 │
│ 3. 选择扫描方式 │
│ (索引扫描 VS 全表) │
│ 4. 子查询优化 │
└───────────────────────┘
↓
执行计划 → 执行器执行为什么需要优化器? 同一个 SQL 可能有多种执行方式。比如:
sql
SELECT * FROM t1 JOIN t2 ON t1.id = t2.id WHERE t1.name = 'xxx'优化器可以选择:先扫描 t1 再关联 t2,或者先扫描 t2 再关联 t1。优化器的任务就是找到成本最低的方案。
关键工具:EXPLAIN 可以查看优化器选择的执行计划。
2.5 执行器(Executor)
优化器 → 执行计划
↓
执行器 → 调用存储引擎 API
│
├── engine.start() ← 打开表、获取锁
├── engine.read_first() ← 读取第一条匹配行
├── engine.read_next() ← 循环读取后续行
└── engine.close() ← 关闭表为什么执行器和引擎需要分两层? 执行器负责控制流程(循环、条件判断),引擎负责数据访问。这样添加新引擎时只需要实现标准 API 即可。
2.6 一条完整 SQL 的执行流程图
┌─────────┐
│ 客户端 │ 发送 SQL
└────┬────┘
│
▼
┌─────────┐ ┌──────────────┐
│ 连接器 │────→│ 连接池/认证 │
└────┬────┘ └──────────────┘
│
▼
┌─────────┐ ┌──────────────┐
│ 查询缓存 │────→│ 命中?返回 │ ← 8.0 移除
└────┬────┘ └──────────────┘
│ 未命中
▼
┌─────────┐ ┌──────────────┐
│ 分析器 │────→│ 词法→语法→语义 │
└────┬────┘ └──────────────┘
│
▼
┌─────────┐ ┌──────────────┐
│ 优化器 │────→│ 选择执行计划 │
└────┬────┘ └──────────────┘
│
▼
┌─────────┐ ┌──────────────┐
│ 执行器 │────→│ 调用引擎 API │
└────┬────┘ └──────┬───────┘
│ │
▼ ▼
┌─────────┐ ┌──────────────┐
│ 存储引擎 │←────│ 读写数据文件 │
└─────────┘ └──────────────┘三、图解全景
MySQL 四层架构
┌─────────────────────────────────┐
│ Connectors (JDBC/ODBC) │
├─────────────────────────────────┤
│ Connection Pool │ ← 连接管理
├─────────────────────────────────┤
│ SQL Interface → Parser │
│ ↓ │ ← 查询处理
│ Optimizer → Executor │
├─────────────────────────────────┤
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │InnoDB│ │MyISAM│ │Memory│ │ ← 可插拔引擎
│ └──────┘ └──────┘ └──────┘ │
├─────────────────────────────────┤
│ Binary Log, 数据文件, 系统表 │ ← 持久化
└─────────────────────────────────┘四、实战验证
查看当前使用的存储引擎
sql
-- 查看默认引擎
SHOW VARIABLES LIKE 'default_storage_engine';
-- 查看表使用的引擎
SHOW TABLE STATUS WHERE Name = 'users'\G
-- 查看支持的引擎
SHOW ENGINES;查看连接状态
sql
-- 查看当前连接数
SHOW STATUS LIKE 'Threads_connected';
-- 查看最大连接数
SHOW VARIABLES LIKE 'max_connections';查看 SQL 执行细节
sql
-- 开启执行器追踪
SET profiling = 1;
-- 执行你的 SQL
SELECT * FROM users WHERE id = 100;
-- 查看执行详情
SHOW PROFILE;五、面试视角
| 追问 | 答案要点 |
|---|---|
| 一条 SELECT 语句的执行过程? | 连接器 → (查询缓存)→ 分析器 → 优化器 → 执行器 → 存储引擎 |
| 为什么 MySQL 需要分层架构? | 解耦查询处理和数据存储;可插拔引擎设计;关注点分离 |
| 查询缓存为什么被移除? | 高并发下频繁失效 + 需要加锁,弊大于利;8.0 移除 |
| 优化器会做哪些优化? | 索引选择、JOIN 顺序、子查询优化、谓词下推 |
| 分析器和优化器有什么区别? | 分析器保证 SQL 语法正确,优化器选择最佳执行路径 |
SHOW PROFILE 能做哪些排查? | 查看 SQL 各阶段耗时,定位瓶颈在解析/优化/执行/引擎哪一层 |
📚 相关链接
- 下一层:**InnoDB架构** — 存储引擎核心实现
- 对比参考:**存储引擎对比** — InnoDB vs MyISAM
- 下游索引:**索引与查询优化**
- ← 返回 **架构与存储引擎索引**