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. 测试验证:在生产环境优化前,先在测试环境验证

相关推荐
橙露3 小时前
Spring Boot 核心原理:自动配置机制与自定义 Starter 开发
java·数据库·spring boot
冰暮流星3 小时前
sql语言之分组语句group by
java·数据库·sql
符哥20083 小时前
Ubuntu 常用指令集大全(附实操实例)
数据库·ubuntu·postgresql
C++ 老炮儿的技术栈4 小时前
Qt 编写 TcpClient 程序 详细步骤
c语言·开发语言·数据库·c++·qt·算法
怣504 小时前
MySQL子查询零基础入门教程:从小白到上手(零基础入门版)
数据库·mysql
码界调试侠4 小时前
MongoDB 常用查询语法
数据库·mongodb
静听山水4 小时前
StarRocks导入数据【Stream Load】
数据库
藦卡机器人4 小时前
国产机械臂做的比较好的品牌有哪些?
大数据·数据库·人工智能
jiunian_cn4 小时前
【Redis】数据库管理操作
数据库·redis·缓存
l1t4 小时前
DeepSeek总结的DuckDB使用 WITH RECURSIVE 和 USING KEY 进行聚合的特性
sql·duckdb