外观
KingBaseES 执行计划分析
执行计划是数据库优化器为SQL查询生成的执行路径,它详细描述了数据库将如何执行查询,包括表的访问顺序、连接方式、索引使用等。深入理解和分析执行计划是数据库性能优化的关键。
执行计划基本概念
什么是执行计划?
执行计划是数据库优化器根据查询语句、表结构、索引信息和统计数据生成的查询执行方案。它决定了:
- 表的访问顺序
- 表的访问方式(全表扫描、索引扫描等)
- 表之间的连接方式(嵌套循环、哈希连接、合并连接等)
- 连接顺序
- 排序方式
- 聚合方式
执行计划的生成过程
- 解析阶段:解析SQL语句,生成解析树
- 优化阶段:优化器根据统计信息和成本模型生成多个候选执行计划
- 选择阶段:选择成本最低的执行计划
- 执行阶段:按照选定的执行计划执行查询
生成执行计划
使用 EXPLAIN 命令
EXPLAIN 命令用于查看SQL查询的执行计划,但不实际执行查询:
sql
EXPLAIN SELECT * FROM employees WHERE department_id = 10;使用 EXPLAIN ANALYZE 命令
EXPLAIN ANALYZE 命令不仅会显示执行计划,还会实际执行查询并显示执行统计信息:
sql
EXPLAIN ANALYZE SELECT * FROM employees WHERE department_id = 10;使用 EXPLAIN VERBOSE 命令
EXPLAIN VERBOSE 命令显示更详细的执行计划信息,包括列的信息:
sql
EXPLAIN VERBOSE SELECT * FROM employees WHERE department_id = 10;使用 EXPLAIN COSTS 命令
EXPLAIN COSTS 命令显示执行计划的成本估算:
sql
EXPLAIN COSTS SELECT * FROM employees WHERE department_id = 10;执行计划解读
执行计划输出格式
KingBaseES 执行计划以树状结构显示,每一行代表一个执行节点,包含以下信息:
- 节点类型:如 Seq Scan、Index Scan、Nested Loop 等
- 扫描对象:如表名或索引名
- 过滤条件:如 "filter: (department_id = 10)"
- 成本信息:如 "cost=0.00..100.00 rows=100 width=100"
- 实际执行信息:(仅 EXPLAIN ANALYZE)如 "actual time=0.010..0.100 rows=100 loops=1"
成本估算解读
成本估算格式:cost=启动成本..总成本 rows=预估行数 width=每行宽度
- 启动成本:生成第一行结果所需的成本
- 总成本:生成所有结果所需的成本
- 预估行数:优化器预估的返回行数
- 每行宽度:每行数据的预估宽度(字节)
实际执行信息解读
实际执行信息格式:actual time=启动时间..总时间 rows=实际行数 loops=循环次数
- 启动时间:生成第一行结果所需的实际时间(毫秒)
- 总时间:生成所有结果所需的实际时间(毫秒)
- 实际行数:实际返回的行数
- 循环次数:该节点执行的次数
常见执行节点类型
表访问节点
1. Seq Scan(全表扫描)
Seq Scan on employees (cost=0.00..200.00 rows=10000 width=100)
Filter: (department_id = 10)全表扫描是最简单的表访问方式,数据库会顺序读取表中的所有行。适用于:
- 表较小
- 查询返回表中大部分数据
- 没有合适的索引
2. Index Scan(索引扫描)
Index Scan using idx_employees_department_id on employees (cost=0.25..8.27 rows=100 width=100)
Index Cond: (department_id = 10)索引扫描先扫描索引,然后根据索引中的指针读取对应的数据行。适用于:
- 查询返回表中少量数据
- 有合适的索引
3. Index Only Scan(仅索引扫描)
Index Only Scan using idx_employees_department_id on employees (cost=0.25..6.25 rows=100 width=20)
Index Cond: (department_id = 10)仅索引扫描只需要扫描索引,不需要回表查询数据。适用于:
- 查询的所有列都包含在索引中
- 索引的可见性映射(VM)标记数据块为全部可见
4. Bitmap Heap Scan + Bitmap Index Scan(位图扫描)
Bitmap Heap Scan on employees (cost=4.25..12.27 rows=100 width=100)
Recheck Cond: (department_id = 10)
-> Bitmap Index Scan on idx_employees_department_id (cost=0.00..4.23 rows=100 width=0)
Index Cond: (department_id = 10)位图扫描结合了索引扫描和全表扫描的优点,适用于:
- 查询返回表中中等数量的数据
- 需要组合多个索引条件
连接节点
1. Nested Loop Join(嵌套循环连接)
Nested Loop (cost=0.25..200.25 rows=1000 width=200)
-> Seq Scan on departments (cost=0.00..100.00 rows=10 width=100)
-> Index Scan using idx_employees_department_id on employees (cost=0.25..10.00 rows=100 width=100)
Index Cond: (department_id = departments.department_id)嵌套循环连接是最基本的连接方式,适用于:
- 外表较小
- 内表有高效的索引
2. Hash Join(哈希连接)
Hash Join (cost=100.00..300.00 rows=1000 width=200)
Hash Cond: (employees.department_id = departments.department_id)
-> Seq Scan on employees (cost=0.00..200.00 rows=10000 width=100)
-> Hash (cost=100.00..100.00 rows=10 width=100)
-> Seq Scan on departments (cost=0.00..100.00 rows=10 width=100)哈希连接使用哈希表进行连接,适用于:
- 两个表都较大
- 连接条件是等值连接
3. Merge Join(合并连接)
Merge Join (cost=200.00..400.00 rows=1000 width=200)
Merge Cond: (employees.department_id = departments.department_id)
-> Sort (cost=150.00..160.00 rows=10000 width=100)
Sort Key: employees.department_id
-> Seq Scan on employees (cost=0.00..200.00 rows=10000 width=100)
-> Sort (cost=50.00..55.00 rows=10 width=100)
Sort Key: departments.department_id
-> Seq Scan on departments (cost=0.00..100.00 rows=10 width=100)合并连接先对两个表进行排序,然后合并排序后的结果。适用于:
- 两个表已经排序或有排序的索引
- 连接条件是等值连接或范围连接
其他节点类型
1. Sort(排序)
Sort (cost=100.00..110.00 rows=4000 width=100)
Sort Key: last_name
-> Seq Scan on employees (cost=0.00..200.00 rows=10000 width=100)用于对结果集进行排序,如 ORDER BY 子句。
2. Aggregate(聚合)
Aggregate (cost=200.00..200.01 rows=1 width=8)
-> Seq Scan on employees (cost=0.00..200.00 rows=10000 width=4)用于聚合操作,如 COUNT、SUM、AVG 等。
3. Limit(限制)
Limit (cost=0.25..8.27 rows=10 width=100)
-> Index Scan using idx_employees_hire_date on employees (cost=0.25..827.00 rows=1000 width=100)
Index Cond: (hire_date > '2020-01-01'::date)用于限制返回的行数,如 LIMIT 子句。
执行计划分析方法
1. 从顶层开始分析
执行计划以树状结构显示,应该从顶层(最上面)开始分析,因为顶层节点代表最终的执行结果。
2. 关注成本估算
成本估算显示了优化器认为该操作的相对成本,成本越高,执行时间可能越长。
3. 检查实际执行情况
使用 EXPLAIN ANALYZE 查看实际执行情况,比较预估行数和实际行数的差异:
- 如果差异较大,说明统计信息可能过时,需要更新统计信息
- 实际时间远大于预估时间,可能存在性能问题
4. 检查表访问方式
- 全表扫描是否必要?
- 是否使用了合适的索引?
- 索引扫描的过滤条件是否高效?
5. 检查连接方式和顺序
- 连接方式是否合适?
- 连接顺序是否合理?
- 是否有可以优化的连接条件?
6. 检查排序和聚合操作
- 排序操作是否必要?
- 是否可以通过索引避免排序?
- 聚合操作的效率如何?
执行计划优化策略
1. 添加合适的索引
根据查询条件和执行计划,添加合适的索引可以显著提高查询性能:
sql
-- 为 department_id 列添加索引
CREATE INDEX idx_employees_department_id ON employees(department_id);
-- 为多列查询添加复合索引
CREATE INDEX idx_employees_dept_salary ON employees(department_id, salary);2. 更新统计信息
过时的统计信息会导致优化器生成低效的执行计划,定期更新统计信息:
sql
-- 更新指定表的统计信息
ANALYZE employees;
-- 更新所有表的统计信息
ANALYZE;3. 优化查询语句
- **避免 SELECT ***:只查询需要的列
- 优化 WHERE 子句:避免在列上使用函数,确保索引可以被使用
- 优化 JOIN 条件:使用等值连接,确保连接列有索引
- 优化 ORDER BY 和 GROUP BY:尽量使用索引避免排序
4. 调整参数
根据执行计划调整相关参数,如:
work_mem:影响排序和哈希连接的性能effective_cache_size:影响优化器的成本估算random_page_cost:影响优化器对索引扫描的成本估算
5. 重新设计表结构
- 分区表:将大表拆分为多个小表
- 垂直分表:将不常用的列分离到单独的表中
- 水平分表:根据业务逻辑将数据分散到多个表中
版本差异 (V8 R6 vs V8 R7)
V8 R6
- 执行计划输出格式较为简单
- 缺少一些高级的执行节点类型
- 优化器的成本模型相对简单
- 缺少执行计划的可视化工具
V8 R7
- 增强了执行计划的输出格式,包含更多详细信息
- 新增了一些执行节点类型,如并行执行节点
- 优化了优化器的成本模型,提高了执行计划的质量
- 新增了执行计划可视化工具
keplan - 支持并行查询执行计划
最佳实践
- 定期分析执行计划:对关键查询定期分析执行计划,及时发现性能问题
- 使用 EXPLAIN ANALYZE:结合实际执行情况分析执行计划
- 更新统计信息:定期更新表的统计信息,确保优化器生成准确的执行计划
- 添加合适的索引:根据执行计划添加合适的索引,避免过度索引
- 优化查询语句:编写高效的查询语句,遵循SQL优化最佳实践
- 监控执行计划变化:关注执行计划的变化,特别是在表结构或数据分布变化后
- 使用参数化查询:减少执行计划的生成开销
- 考虑并行查询:对于大型查询,考虑使用并行执行提高性能
常见问题 (FAQ)
Q1: 为什么执行计划会变化?
A: 执行计划变化的原因包括:
- 表数据量或分布发生变化
- 统计信息更新
- 索引的创建或删除
- 参数设置的变化
- 数据库版本升级
Q2: 预估行数和实际行数差异很大怎么办?
A: 这种情况通常是由于统计信息过时导致的,解决方案:
- 更新表的统计信息:
ANALYZE table_name - 对于大表,可以使用
ANALYZE VERBOSE table_name查看详细信息
Q3: 如何强制使用特定的执行计划?
A: 可以使用计划 hints 或修改查询语句来影响执行计划:
- 计划 hints:使用
/*+ HINT */语法提示优化器 - 修改查询语句:调整查询条件或连接顺序
- 临时禁用索引:使用
SET enable_indexscan = OFF等参数
Q4: 为什么有时候全表扫描比索引扫描更快?
A: 全表扫描比索引扫描更快的情况:
- 查询返回表中大部分数据(通常超过 20-30%)
- 表较小,全表扫描的I/O成本更低
- 索引碎片化严重
- 统计信息不准确
Q5: 如何查看并行执行计划?
A: 在 V8 R7 中,使用 EXPLAIN ANALYZE 可以查看并行执行计划:
sql
EXPLAIN ANALYZE SELECT count(*) FROM employees;并行执行计划会显示 Parallel Seq Scan 等并行执行节点。
Q6: 如何使用 keplan 工具可视化执行计划?
A: keplan 是 V8 R7 新增的执行计划可视化工具:
bash
# 生成执行计划文件
export KE_PLAN_OUT=plan.json
execsql -c "EXPLAIN (FORMAT JSON) SELECT * FROM employees;"
# 使用 keplan 查看执行计划
keplan plan.json总结
执行计划分析是数据库性能优化的核心技能,通过深入理解执行计划的生成过程、解读方法和优化策略,可以显著提高SQL查询的性能。DBA应该掌握执行计划的分析方法,定期对关键查询进行分析和优化,确保数据库系统的高效运行。
执行计划分析是一个持续的过程,需要结合实际业务场景、数据分布和系统资源进行综合考虑。随着数据库版本的更新,执行计划的生成和优化技术也在不断发展,DBA需要持续学习和关注新技术,不断提升数据库性能优化能力。
