首页 / 知识库 / python服务端进阶 / 数据库优化-索引与分库分表

03 - 用 EXPLAIN 读懂执行计划

优化 SQL 不能靠猜。EXPLAIN 是数据库给你的「透视镜」——它告诉你数据库打算怎么执行这条 SQL:走不走索引、要扫描多少行、慢在哪。学会看它,你就有了优化的「眼睛」。

一句话先记住:EXPLAIN 让你看到数据库的「执行计划」,从此优化 SQL 从「凭感觉」变成「看数据」。


3.1 EXPLAIN 和 EXPLAIN ANALYZE

-- EXPLAIN:只「预估」执行计划,不真正执行(快,安全)
EXPLAIN SELECT * FROM users WHERE email = 'a@x.com';

-- EXPLAIN ANALYZE:真正执行一遍,给出「实际」耗时和行数(更准,但会真的跑)
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'a@x.com';
命令是否真执行用途
EXPLAIN否,只估算快速看计划、看走不走索引
EXPLAIN ANALYZE是,真跑看真实耗时、估算准不准

⚠️ EXPLAIN ANALYZEUPDATE/DELETE真的执行!要在事务里跑并回滚,或只在测试库用。


3.2 最该看的:扫描方式

执行计划里最关键的信息,是数据库用什么方式「找数据」。PostgreSQL 常见几种:

Seq Scan(顺序扫描 / 全表扫描)
  → 一行行看整张表。大表上出现它,通常是「没走索引」的信号 ⚠️

Index Scan(索引扫描)
  → 走了索引定位,再回表取数据。通常是好事 ✅

Index Only Scan(仅索引扫描)
  → 走了索引,且数据全在索引里,不用回表。最理想 ✅✅(覆盖索引生效)

Bitmap Index Scan / Bitmap Heap Scan
  → 介于两者之间,匹配行较多时用,先收集位置再批量取

看执行计划第一眼:大表上是不是出现了 Seq Scan?如果是,且这条查询应该走索引,那就是优化点。


3.3 看懂关键数字

以一段(简化的)EXPLAIN 输出为例:

Index Scan using idx_email on users
  (cost=0.42..8.44 rows=1 width=64) (actual time=0.03..0.04 rows=1 loops=1)

拆解几个关键字段:

字段含义怎么看
Index Scan using idx_email用了 idx_email 这个索引确认走了哪个索引 ✅
cost=0.42..8.44估算的启动成本..总成本数值越大越慢(相对值,用于比较)
rows=1估算要处理的行数越少越好
actual time=0.03..0.04实际耗时(毫秒)只有 ANALYZE 才有
actual ... rows=1实际处理的行数和估算 rows 对比

一个重要技巧:对比「估算行数」和「实际行数」

如果 估算 rows=1,但 实际 rows=100000
  → 说明数据库的「统计信息过时了」,估错了,可能选了错的计划
  → 解决:ANALYZE users;  (更新统计信息,让优化器估得准)

3.4 一个优化前后的对比示例

-- 优化前:email 没索引
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'a@x.com';
Seq Scan on users  (cost=0.00..18334 rows=1)
  (actual time=120.5..120.5 rows=1)      ← 全表扫描,扫了整张表,120ms
-- 建索引
CREATE INDEX idx_email ON users (email);

-- 优化后
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'a@x.com';
Index Scan using idx_email on users  (cost=0.42..8.44 rows=1)
  (actual time=0.03..0.04 rows=1)        ← 走索引,0.04ms,快了几千倍

对比一目了然:Seq ScanIndex Scan,耗时从 120ms 到 0.04ms。这就是用 EXPLAIN 验证优化效果的标准姿势。


3.5 还要留意的几个「慢信号」

· Seq Scan on 大表        → 该查询可能缺索引
· Nested Loop + 大 rows   → JOIN 可能没用好索引,数据量大时很慢
· Sort(排序)耗时高       → ORDER BY 的列可以考虑建索引来免排序
· 估算 rows 和实际差很大    → 统计信息过时,ANALYZE 一下
· Rows Removed by Filter 很大 → 扫了很多行但大部分被过滤掉,索引不够精准

3.6 使用建议

优化任何一条慢 SQL 的标准动作:
  1. EXPLAIN ANALYZE 跑一遍,看扫描方式和耗时
  2. 是 Seq Scan 且不该是 → 建索引 / 改写 SQL
  3. 改完再 EXPLAIN ANALYZE,对比前后
  4. 关注实际耗时 actual time 有没有真的降下来

心态:不要凭感觉说「加个索引应该快了」。用 EXPLAIN ANALYZE 拿数据说话,优化才靠谱。


本章小结

你要记住要点
两个命令EXPLAIN(估算)、EXPLAIN ANALYZE(真跑,有实际耗时)
看扫描方式Seq Scan(全表,警惕)、Index Scan(走索引,好)、Index Only Scan(免回表,最好)
看关键数字cost、估算 rows vs 实际 rows、actual time
统计信息估算和实际差很大就 ANALYZE 更新统计
核心心态优化用数据验证,别凭感觉

下一章预告:索引三章讲完,能解决大部分问题。但优化不只有索引——SQL 写法、表结构设计本身也大有讲究。


上一章 ← 02 - 索引优化实战 | 下一章 → 04 - SQL 与表结构优化