SQL优化及实战分享
索引是优化核心:优先使用复合索引,遵循最左匹配原则,避免过度索引;执行计划是优化前提:任何优化前先执行EXPLAIN,明确慢查询的根因;减少不必要的开销:避免SELECT *、文件排序、全表扫描,精准查询所需数据。
·
1. sql优化教程


2. sql实战
SQL优化实战教学:从慢查询到高性能
一、SQL优化核心原则
优化的核心目标:减少数据库IO、降低CPU计算、避免全表扫描,最终让查询在最优路径下执行。
优化前必须掌握的基础:
- 学会查看执行计划(EXPLAIN)
- 理解索引的工作原理(B+树)
- 知道常见的慢查询场景
二、实战环境准备
1. 测试表创建
-- 创建用户订单表(模拟百万级数据场景)
CREATE TABLE `order_info` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
`order_no` VARCHAR(64) NOT NULL COMMENT '订单号',
`user_id` BIGINT NOT NULL COMMENT '用户ID',
`order_amount` DECIMAL(10,2) NOT NULL COMMENT '订单金额',
`order_status` TINYINT NOT NULL COMMENT '订单状态:1-待支付 2-已支付 3-已取消',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`update_time` DATETIME NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户订单表';
-- 插入测试数据(可通过存储过程批量插入,这里简化)
INSERT INTO `order_info` (order_no, user_id, order_amount, order_status, create_time, update_time)
VALUES
('ORD20260310001', 1001, 99.90, 2, '2026-03-10 10:00:00', '2026-03-10 10:05:00'),
('ORD20260310002', 1002, 199.00, 1, '2026-03-10 11:00:00', '2026-03-10 11:00:00'),
('ORD20260310003', 1001, 299.99, 2, '2026-03-10 12:00:00', '2026-03-10 12:10:00');
2. 查看执行计划的方法
-- 核心命令:EXPLAIN + 你的SQL
EXPLAIN SELECT * FROM order_info WHERE user_id = 1001 AND order_status = 2;
执行计划关键字段说明:
type:访问类型(ALL-全表扫描,ref-非唯一索引扫描,range-范围扫描,const-主键/唯一索引)key:实际使用的索引(NULL表示未使用索引)rows:预估扫描行数Extra:额外信息(Using filesort/Using temporary 是性能杀手)
三、核心优化场景实战
场景1:避免全表扫描(添加合适索引)
问题SQL(无索引)
-- 需求:查询用户1001的所有已支付订单
SELECT * FROM order_info WHERE user_id = 1001 AND order_status = 2;
执行计划分析
type: ALL(全表扫描)key: NULL(未使用索引)rows: 预估扫描全表行数
优化方案:添加复合索引
-- 创建复合索引(遵循最左匹配原则)
CREATE INDEX idx_user_status ON order_info(user_id, order_status);
-- 再次执行查询(索引生效)
EXPLAIN SELECT * FROM order_info WHERE user_id = 1001 AND order_status = 2;
优化后执行计划变化
type: ref(索引扫描)key: idx_user_status(使用创建的复合索引)rows: 仅扫描匹配的行数
场景2:优化排序(避免Using filesort)
问题SQL(排序无索引)
-- 需求:查询用户1001的订单并按创建时间降序排列
SELECT * FROM order_info WHERE user_id = 1001 ORDER BY create_time DESC;
执行计划分析
Extra: Using filesort(文件排序,性能差)
优化方案:添加包含排序字段的复合索引
-- 创建覆盖查询+排序的复合索引
CREATE INDEX idx_user_create ON order_info(user_id, create_time DESC);
-- 优化后查询
EXPLAIN SELECT * FROM order_info WHERE user_id = 1001 ORDER BY create_time DESC;
优化效果
Extra: 无Using filesort(通过索引排序,无需额外计算)
场景3:避免SELECT *(使用覆盖索引)
问题SQL
-- 仅需要订单号和金额,却查询所有字段
SELECT * FROM order_info WHERE user_id = 1001 AND order_status = 2;
优化方案:精准查询+覆盖索引
-- 1. 精准查询需要的字段
SELECT order_no, order_amount FROM order_info WHERE user_id = 1001 AND order_status = 2;
-- 2. 创建覆盖索引(包含查询的所有字段)
CREATE INDEX idx_user_status_amount ON order_info(user_id, order_status, order_no, order_amount);
-- 3. 执行计划验证
EXPLAIN SELECT order_no, order_amount FROM order_info WHERE user_id = 1001 AND order_status = 2;
优化效果
Extra: Using index(覆盖索引,无需回表查询,IO大幅降低)
场景4:优化JOIN查询(关联字段加索引)
准备关联表
CREATE TABLE `user_info` (
`user_id` BIGINT NOT NULL COMMENT '用户ID',
`user_name` VARCHAR(32) NOT NULL COMMENT '用户名',
`phone` VARCHAR(16) NOT NULL COMMENT '手机号',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';
INSERT INTO `user_info` (user_id, user_name, phone) VALUES (1001, '张三', '13800138000');
问题SQL(关联字段无索引)
-- 关联查询用户和订单信息
SELECT u.user_name, o.order_no, o.order_amount
FROM order_info o
JOIN user_info u ON o.user_id = u.user_id
WHERE o.order_status = 2;
优化方案:关联字段加索引
-- 给order_info的user_id加索引(如果未加)
CREATE INDEX idx_user_id ON order_info(user_id);
-- 优化后查询
EXPLAIN SELECT u.user_name, o.order_no, o.order_amount
FROM order_info o
JOIN user_info u ON o.user_id = u.user_id
WHERE o.order_status = 2;
核心原则
- JOIN的关联字段必须加索引
- 小表驱动大表(InnoDB会自动优化,但尽量遵循)
场景5:优化IN/NOT IN(替换为EXISTS)
问题SQL(NOT IN性能差)
-- 查询没有下过订单的用户
SELECT * FROM user_info u
WHERE u.user_id NOT IN (SELECT user_id FROM order_info);
优化方案:使用EXISTS
SELECT * FROM user_info u
WHERE NOT EXISTS (SELECT 1 FROM order_info o WHERE o.user_id = u.user_id);
优化原理
- IN/NOT IN会导致全表扫描,EXISTS会利用索引快速匹配
- EXISTS只要找到匹配行就停止扫描,效率更高
四、常用优化工具
1. 慢查询日志(开启方法)
# 临时开启(重启失效)
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1; # 记录执行时间>1秒的SQL
SET GLOBAL slow_query_log_file = '/var/lib/mysql/slow.log'; # 日志路径
# 查看慢查询日志
mysqldumpslow -s t /var/lib/mysql/slow.log # 按执行时间排序
2. 索引优化建议
-- 查看索引使用情况
SHOW INDEX FROM order_info;
-- 分析表(统计数据更新)
ANALYZE TABLE order_info;
-- 查看未使用的索引(MySQL 8.0+)
SELECT * FROM sys.schema_unused_indexes;
五、避坑指南
- 不要给所有字段加索引:索引会增加写入开销(INSERT/UPDATE/DELETE)
- 避免使用%开头的模糊查询:
LIKE '%abc'会失效索引,LIKE 'abc%'可使用索引 - 避免在索引字段上做函数操作:
WHERE DATE(create_time) = '2026-03-10'会失效索引,改为WHERE create_time >= '2026-03-10 00:00:00' AND create_time < '2026-03-11 00:00:00' - 避免大事务:长事务会锁表,影响并发性能
总结
- 索引是优化核心:优先使用复合索引,遵循最左匹配原则,避免过度索引;
- 执行计划是优化前提:任何优化前先执行EXPLAIN,明确慢查询的根因;
- 减少不必要的开销:避免SELECT *、文件排序、全表扫描,精准查询所需数据。
关键优化口诀
- 索引优先,覆盖最佳;
- 避免全扫,排序靠索引;
- 精准查询,少用*号;
- 先看执行计划,再动手优化。
更多推荐



所有评论(0)