前置知识: SQL

执行计划

00:00
4 min Advanced 2026/6/14

SQL执行计划:EXPLAIN与EXPLAIN ANALYZE的输出解读、扫描类型、连接策略与性能诊断

1. 执行计划概述

执行计划(Execution Plan)是数据库优化器为 SQL 查询选择的执行策略。理解执行计划是 SQL 性能优化的核心技能。

1.1 优化器类型

类型说明
基于规则(RBO)根据预定义规则选择执行计划
基于代价(CBO)估算各方案代价,选择最优方案

现代数据库主要使用 CBO,RBO 作为后备。

2. EXPLAIN 语法

2.1 各数据库语法

-- PostgreSQL
EXPLAIN SELECT * FROM employees WHERE dept_id = 5;
EXPLAIN ANALYZE SELECT * FROM employees WHERE dept_id = 5;  -- 实际执行

-- MySQL
EXPLAIN SELECT * FROM employees WHERE dept_id = 5;
EXPLAIN ANALYZE SELECT * FROM employees WHERE dept_id = 5;  -- MySQL 8.0+

-- SQL Server
SET SHOWPLAN_TEXT ON;
SELECT * FROM employees WHERE dept_id = 5;

-- Oracle
EXPLAIN PLAN FOR SELECT * FROM employees WHERE dept_id = 5;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

2.2 EXPLAIN vs EXPLAIN ANALYZE

特性EXPLAINEXPLAIN ANALYZE
执行查询
估算代价
实际时间
实际行数
副作用DML 会实际执行

3. PostgreSQL 执行计划解读

3.1 基本输出

EXPLAIN ANALYZE SELECT * FROM employees WHERE dept_id = 5;

-- 输出:
-- Seq Scan on employees  (cost=0.00..15.50 rows=5 width=68) (actual time=0.01..0.03 rows=5 loops=1)
--   Filter: (dept_id = 5)
--   Rows Removed by Filter: 45
-- Planning Time: 0.05 ms
-- Execution Time: 0.05 ms

关键字段解读

字段含义
cost=X..YX=启动代价,Y=总代价(估算)
rows=N估算返回行数
width=N估算每行平均字节数
actual time实际执行时间(毫秒)
actual rows实际返回行数
loops执行次数

3.2 扫描类型

-- 顺序扫描(Seq Scan):全表扫描
Seq Scan on employees
-- 适合:小表、大部分行需要返回

-- 索引扫描(Index Scan):使用B+树索引
Index Scan using idx_employees_dept on employees
-- 适合:选择性高的查询

-- 仅索引扫描(Index Only Scan):覆盖索引
Index Only Scan using idx_employees_dept_name on employees
-- 适合:索引包含所有需要的列

-- 位图扫描(Bitmap Heap Scan + Bitmap Index Scan)
Bitmap Heap Scan on employees
  -> Bitmap Index Scan on idx_employees_dept
-- 适合:选择性中等,返回多行

-- 并行扫描(Parallel Seq Scan)
Parallel Seq Scan on employees
  Workers: 2
-- 适合:大表扫描

3.3 连接策略

-- Nested Loop Join
Nested Loop
  -> Index Scan on departments
  -> Index Scan on employees
-- 适合:小表驱动大表

-- Hash Join
Hash Join
  -> Seq Scan on departments
  -> Hash
    -> Seq Scan on employees
-- 适合:大表等值连接

-- Merge Join
Merge Join
  -> Index Scan on departments
  -> Index Scan on employees
-- 适合:已排序数据

4. MySQL 执行计划解读

4.1 EXPLAIN 输出列

EXPLAIN SELECT * FROM employees WHERE dept_id = 5;
含义
id查询标识符
select_type查询类型(SIMPLE, PRIMARY, SUBQUERY)
table访问的表
partitions匹配的分区
type访问类型(最重要)
possible_keys可能使用的索引
key实际使用的索引
key_len使用的索引
ref索引比较的列
rows估算扫描
filtered过滤比例
Extra额外信息

4.2 type 列(访问类型)

从优到劣排序

type说明索引使用
system中只有一
const匹配一主键/唯一索引精确匹配
eq_ref匹配一主键/唯一索引精确匹配
ref匹配(非唯一索引前缀匹配
range范围扫描范围条件
index索引扫描索引
ALL全表扫描索引
-- const:主键等值查询
EXPLAIN SELECT * FROM employees WHERE id = 1;
-- type: const

-- ref:非唯一索引等值查询
EXPLAIN SELECT * FROM employees WHERE dept_id = 5;
-- type: ref

-- range:范围查询
EXPLAIN SELECT * FROM employees WHERE salary > 50000;
-- type: range

-- ALL:全表扫描
EXPLAIN SELECT * FROM employees WHERE YEAR(created_at) = 2026;
-- type: ALL(函数导致索引失效)

4.3 Extra 列关键信息

Extra 含义
Using index覆盖索引,无需回
Using where服务层过滤
Using index condition索引下推(ICP)
Using temporary使用临时
Using filesort额外排序(非索引排序)
Using join buffer使用连接缓冲区
Impossible WHEREWHERE 条件不可能为真

5. 执行计划诊断

5.1 估算 vs 实际

-- PostgreSQL:对比估算与实际
EXPLAIN ANALYZE SELECT * FROM employees WHERE dept_id = 5;

-- 估算 rows=5 vs 实际 rows=5000
-- 说明统计信息过时,需要 ANALYZE
ANALYZE employees;

5.2 常见问题与解决

-- 问题1:全表扫描
-- 原因:缺少索引或索引失效
-- 解决:创建索引或改写查询
CREATE INDEX idx_employees_dept ON employees(dept_id);

-- 问题2:Using filesort
-- 原因:排序无法利用索引
-- 解决:创建排序索引
CREATE INDEX idx_employees_dept_salary ON employees(dept_id, salary DESC);

-- 问题3:Using temporary
-- 原因:GROUP BY/DISTINCT 需要临时表
-- 解决:优化 GROUP BY 列顺序,使其与索引一致

-- 问题4:rows 估算偏差大
-- 原因:统计信息过时
-- 解决:更新统计信息
ANALYZE employees;  -- PostgreSQL
ANALYZE TABLE employees;  -- MySQL

5.3 强制/提示索引

-- PostgreSQL:禁用顺序扫描
SET enable_seqscan = off;

-- MySQL:USE INDEX / FORCE INDEX
SELECT * FROM employees USE INDEX (idx_dept) WHERE dept_id = 5;
SELECT * FROM employees FORCE INDEX (idx_dept) WHERE dept_id = 5;

-- Oracle:提示
SELECT /*+ INDEX(e idx_dept) */ * FROM employees e WHERE dept_id = 5;

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式