理解 SQL 是如何一步步被 MySQL 执行的
掌握优化器的执行计划选择逻辑
深入理解 EXPLAIN 输出字段含义及分析技巧
学会判断 SQL 慢的根因并优化写法
客户端 → 连接线程
↓
查询缓存(已废弃)
↓
SQL 解析器(词法 + 语法)
↓
预处理器(检查字段、权限等)
↓
优化器(选择执行计划)
↓
执行器(调用存储引擎)
↓
返回结果
最核心部分是:优化器 + 执行器 + 存储引擎协作。
优化器的职责是:为 SQL 语句选择最优的执行路径。
哪个索引更好用(成本更低)
是否走索引合并 / 排序
是否可以下推过滤条件(ICP)
多表查询时的 join 顺序
表记录数(cardinality)
索引区分度(选择性)
是否存在索引覆盖
优化器不懂业务,它只看“代价低不低”,所以有时选的计划你觉得很傻。
EXPLAIN SELECT * FROM user WHERE id = 1;
字段 | 含义 |
---|---|
id | 查询中每个 SELECT 子句的标识 |
select_type | 简单查询/子查询/联合等 |
table | 哪张表 |
type | 访问类型:ALL > index > range > ref > eq_ref > const(越靠右越好) |
key | 选中的索引名 |
key_len | 索引使用了多少字节 |
rows | 预估扫描行数 |
Extra | 附加信息:Using where / Using index / Using filesort / Using temporary 等 |
ALL
表示全表扫描,要优化!
index
虽然是走索引,但依然扫整棵索引树,数据量大也慢。
为 NULL 表示没有用到任何索引。
Using filesort
(慢)
Using temporary
(可能用磁盘临时表)
Using index
表示覆盖索引,这是性能好的一种提示。
-- 慢:跳过大量数据
SELECT * FROM orders ORDER BY id LIMIT 100000, 10;
-- 快:用“上次最大 id”做游标
SELECT * FROM orders WHERE id > ? ORDER BY id LIMIT 10;
-- 慢:OR 尽量分开
SELECT * FROM t WHERE a = 1 OR b = 2;
-- 快:使用 UNION
SELECT * FROM t WHERE a = 1
UNION
SELECT * FROM t WHERE b = 2;
EXPLAIN SELECT * FROM emp WHERE dept_id = 10;
✅ 思考:
是否命中索引?
rows 预估值是否异常大?
Extra 是否包含 Using filesort?
哪种 type 类型最好?ALL / range / ref / const / eq_ref
?
✅ eq_ref 最优(如:唯一索引+主键 join)
rows=1000000 是否说明真的查这么多行?
❌ rows 是估算值(根据统计信息)
key = NULL
是什么问题?
✅ 没有索引可用,或索引未命中(如类型转换)
用户SQL
↓
解析器(词法语法)
↓
预处理(字段校验/权限)
↓
优化器
- 选索引
- 排序方式
- Join 顺序
↓
执行器 → 存储引擎 → 返回数据
MySQL 查询快不快,很大程度取决于优化器选的执行计划好不好。
下一章我们将进入 第5章 锁机制,包括:
行锁 vs 表锁 vs 意向锁
死锁的本质与排查技巧
如何理解 MVCC 与锁的关系