SQL执行计划与优化详解

一、什么是SQL执行计划

执行计划是数据库优化器生成的、用于执行SQL查询的详细步骤说明。它描述了:

  • 如何访问数据(全表扫描、索引扫描等)
  • 表的连接顺序和方式
  • 操作执行的顺序

二、如何查看执行计划

1. MySQL

sql 复制代码
-- 基本执行计划
EXPLAIN SELECT * FROM users WHERE age > 25;

-- 详细执行计划(MySQL 8.0+)
EXPLAIN ANALYZE SELECT * FROM users WHERE age > 25;

-- JSON格式查看
EXPLAIN FORMAT=JSON SELECT * FROM users;

2. PostgreSQL

sql 复制代码
-- 基本计划
EXPLAIN SELECT * FROM users WHERE age > 25;

-- 带实际执行时间
EXPLAIN ANALYZE SELECT * FROM users WHERE age > 25;

-- 带统计信息
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM users;

3. Oracle

sql 复制代码
-- 生成执行计划
EXPLAIN PLAN FOR 
SELECT * FROM users WHERE age > 25;

-- 查看执行计划
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

4. SQL Server

sql 复制代码
-- 文本格式
SET SHOWPLAN_TEXT ON;
GO
SELECT * FROM users WHERE age > 25;
GO
SET SHOWPLAN_TEXT OFF;

-- 图形化(SSMS中)
-- 点击"显示估计的执行计划"按钮或Ctrl+L

三、执行计划关键元素解读

1. 访问方法(Access Methods)

复制代码
- Seq Scan/Full Table Scan: 全表扫描
- Index Scan: 索引扫描
- Index Only Scan: 仅索引扫描
- Bitmap Index Scan: 位图索引扫描
- Hash Scan: 哈希扫描

2. 连接类型(Join Types)

复制代码
- Nested Loop Join: 嵌套循环连接
- Hash Join: 哈希连接
- Merge Join: 合并连接

3. 关键指标

复制代码
- cost: 预估成本(越低越好)
- rows: 预估返回行数
- width: 预估行宽度
- actual time: 实际执行时间
- loops: 循环次数

四、执行计划优化实战

示例分析:识别问题

sql 复制代码
-- 问题查询
EXPLAIN ANALYZE
SELECT u.name, o.order_date, p.product_name
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN products p ON o.product_id = p.id
WHERE u.city = '北京'
AND o.order_date >= '2024-01-01'
ORDER BY o.order_date DESC
LIMIT 100;

优化步骤:

1. 识别全表扫描
sql 复制代码
-- 检查是否缺少索引
EXPLAIN SELECT * FROM users WHERE city = '北京';
-- 如果显示"Seq Scan",考虑创建索引
CREATE INDEX idx_users_city ON users(city);
2. 优化连接顺序
sql 复制代码
-- 查看连接顺序是否合理
-- 小表驱动大表更高效
-- 使用子查询优化
SELECT * FROM (
  SELECT * FROM orders 
  WHERE order_date >= '2024-01-01'
) o
JOIN users u ON o.user_id = u.id
WHERE u.city = '北京';
3. 索引优化组合
sql 复制代码
-- 复合索引(最左前缀原则)
CREATE INDEX idx_orders_user_date ON orders(user_id, order_date DESC);

-- 覆盖索引(包含查询所有列)
CREATE INDEX idx_users_city_name ON users(city) INCLUDE (name);

-- 函数索引
CREATE INDEX idx_lower_email ON users(LOWER(email));
4. 统计信息更新
sql 复制代码
-- 确保统计信息准确
ANALYZE users;  -- PostgreSQL
ANALYZE TABLE users;  -- MySQL
EXEC sp_updatestats;  -- SQL Server

五、常见优化模式

模式1:N+1查询问题

sql 复制代码
-- 反模式:多次查询
SELECT * FROM users WHERE city = '北京';
-- 对每个user执行:
SELECT * FROM orders WHERE user_id = ?;

-- 优化:使用JOIN
SELECT u.*, o.* 
FROM users u 
LEFT JOIN orders o ON u.id = o.user_id 
WHERE u.city = '北京';

模式2:分页优化

sql 复制代码
-- 低效的分页(OFFSET越大越慢)
SELECT * FROM orders ORDER BY id LIMIT 10 OFFSET 1000000;

-- 优化:使用seek方法
SELECT * FROM orders 
WHERE id > last_seen_id 
ORDER BY id 
LIMIT 10;

模式3:避免SELECT *

sql 复制代码
-- 不推荐
SELECT * FROM users WHERE age > 25;

-- 推荐:只选择需要的列
SELECT id, name, email FROM users WHERE age > 25;

六、优化工具和技巧

1. 数据库内置工具

sql 复制代码
-- PostgreSQL: 检查索引使用情况
SELECT * FROM pg_stat_user_indexes;

-- MySQL: 查看索引统计
SHOW INDEX FROM users;

-- MySQL: 性能分析
SET profiling = 1;
SELECT ...;
SHOW PROFILES;
SHOW PROFILE FOR QUERY 1;

2. 查询重写技巧

sql 复制代码
-- OR优化
SELECT * FROM users WHERE age < 20 OR age > 60;
-- 重写为
SELECT * FROM users WHERE age < 20
UNION
SELECT * FROM users WHERE age > 60;

-- IN优化
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders);
-- 重写为
SELECT u.* FROM users u 
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);

3. 配置优化

sql 复制代码
-- 调整内存设置
-- PostgreSQL: work_mem, shared_buffers
-- MySQL: innodb_buffer_pool_size, sort_buffer_size

-- 启用并行查询
-- PostgreSQL: max_parallel_workers_per_gather
-- MySQL 8.0+: optimizer_switch='parallel_query=on'

七、执行计划可视化工具

  1. pgAdmin / DBeaver:内置可视化执行计划
  2. Explain.dalibo.com:PostgreSQL执行计划可视化
  3. MySQL Workbench:Visual Explain功能
  4. Oracle SQL Developer:执行计划图形化

最佳实践总结

  1. 先看执行计划再优化:不要盲目添加索引

  2. 关注高成本操作:全表扫描、排序、临时表

  3. 索引设计原则

    • 为WHERE、JOIN、ORDER BY列创建索引
    • 使用复合索引减少索引数量
    • 避免过度索引影响写入性能
  4. 定期分析

    sql 复制代码
    -- 找出最慢的查询
    SELECT query, total_time 
    FROM pg_stat_statements 
    ORDER BY total_time DESC 
    LIMIT 10;
  5. 测试验证:在生产环境优化前,先在测试环境验证

相关推荐
Dragon online2 小时前
数据分析师成长之路--从SQL恐惧到数据掌控者的蜕变
数据库·sql
VX:Fegn08952 小时前
计算机毕业设计|基于springboot + vue音乐管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
一招定胜负2 小时前
navicat连接数据库&mysql常见语句及操作
数据库·mysql
热心市民蟹不肉3 小时前
黑盒漏洞扫描(三)
数据库·redis·安全·缓存
chian_ocean3 小时前
openEuler集群 Chrony 时间同步实战:从零构建高精度分布式时钟体系
数据库
Databend3 小时前
构建海量记忆:基于 Databend 的 2C Agent 平台 | 沉浸式翻译 @ Databend meetup 上海站回顾及思考
数据库
Li.CQ3 小时前
SQL学习笔记
笔记·sql·学习
αSIM0V3 小时前
数据库期末重点
数据库·软件工程
2301_800256114 小时前
【第九章知识点总结1】9.1 Motivation and use cases 9.2 Conceptual model
java·前端·数据库
不会写程序的未来程序员4 小时前
Redis 的核心机制(线程模型、原子性、Bigkey、单线程设计原因等)
数据库·redis