EXPLAIN 分析:理解查询计划
EXPLAIN 基础
sql
EXPLAIN SELECT * FROM users WHERE email = 'alice@example.com';输出(PostgreSQL):
text
Seq Scan on users (cost=0.00..25.00 rows=1 width=100)
Filter: (email = 'alice@example.com'::text)- Seq Scan:顺序扫描(全表扫描)
- cost:估算成本(启动成本..总成本)
- rows:预计返回行数
- width:平均行宽度(字节)
EXPLAIN ANALYZE
sql
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'alice@example.com';输出:
text
Seq Scan on users (cost=0.00..25.00 rows=1 width=100) (actual time=0.012..0.045 rows=1 loops=1)
Filter: (email = 'alice@example.com'::text)
Planning Time: 0.123 ms
Execution Time: 0.089 ms- actual time:实际执行时间
- rows:实际返回行数
- loops:执行次数
常见执行计划
Index Scan:索引扫描
sql
CREATE INDEX idx_email ON users(email);
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'alice@example.com';text
Index Scan using idx_email on users (cost=0.29..8.30 rows=1 width=100)
Index Cond: (email = 'alice@example.com'::text)Index Only Scan:覆盖索引
sql
CREATE INDEX idx_email_name ON users(email, name);
EXPLAIN SELECT email, name FROM users WHERE email = 'alice@example.com';text
Index Only Scan using idx_email_name on users不需要访问表,只读索引。
Nested Loop:嵌套循环
sql
EXPLAIN SELECT * FROM orders o JOIN users u ON o.user_id = u.id;text
Nested Loop
-> Seq Scan on orders
-> Index Scan using users_pkey on usersHash Join:哈希连接
text
Hash Join
Hash Cond: (o.user_id = u.id)
-> Seq Scan on orders
-> Hash
-> Seq Scan on usersMySQL EXPLAIN
sql
EXPLAIN SELECT * FROM users WHERE email = 'alice@example.com'\G输出:
text
id: 1
select_type: SIMPLE
table: users
type: ref
possible_keys: idx_email
key: idx_email
key_len: 767
ref: const
rows: 1
Extra: Using index condition关键字段:
- type:ALL(全表)< index < range < ref < eq_ref < const
- key:实际使用的索引
- rows:扫描行数
- Extra:额外信息
识别性能问题
全表扫描
text
Seq Scan on large_table (cost=0.00..10000.00 rows=1000000)解决:创建索引
估算偏差
text
estimated rows=1000, actual rows=100000解决:ANALYZE 更新统计信息
不使用索引
sql
-- 函数包裹列
WHERE YEAR(order_date) = 2026 -- 不使用索引
-- 改写
WHERE order_date BETWEEN '2026-01-01' AND '2026-12-31' -- 使用索引练习
- 使用 EXPLAIN 分析简单查询
- 对比有无索引的执行计划
- 使用 EXPLAIN ANALYZE 查看实际时间
- 分析 JOIN 查询的执行计划
- 识别未使用索引的查询
- 分析复杂查询的性能瓶颈
- 使用 ANALYZE 更新统计信息
- 优化慢查询
完成练习后,打开本章答案页核对。
下一步
下一节学习查询优化:常见技巧和最佳实践。