SQL优化及实战分享

1. sql优化教程

2. sql实战

SQL优化实战教学:从慢查询到高性能

一、SQL优化核心原则

优化的核心目标:减少数据库IO、降低CPU计算、避免全表扫描 ,最终让查询在最优路径下执行。

优化前必须掌握的基础:

  • 学会查看执行计划(EXPLAIN)
  • 理解索引的工作原理(B+树)
  • 知道常见的慢查询场景

二、实战环境准备

1. 测试表创建

sql 复制代码
-- 创建用户订单表(模拟百万级数据场景)
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. 查看执行计划的方法

sql 复制代码
-- 核心命令: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(无索引)
sql 复制代码
-- 需求:查询用户1001的所有已支付订单
SELECT * FROM order_info WHERE user_id = 1001 AND order_status = 2;
执行计划分析
  • type: ALL(全表扫描)
  • key: NULL(未使用索引)
  • rows: 预估扫描全表行数
优化方案:添加复合索引
sql 复制代码
-- 创建复合索引(遵循最左匹配原则)
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(排序无索引)
sql 复制代码
-- 需求:查询用户1001的订单并按创建时间降序排列
SELECT * FROM order_info WHERE user_id = 1001 ORDER BY create_time DESC;
执行计划分析
  • Extra: Using filesort(文件排序,性能差)
优化方案:添加包含排序字段的复合索引
sql 复制代码
-- 创建覆盖查询+排序的复合索引
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
sql 复制代码
-- 仅需要订单号和金额,却查询所有字段
SELECT * FROM order_info WHERE user_id = 1001 AND order_status = 2;
优化方案:精准查询+覆盖索引
sql 复制代码
-- 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查询(关联字段加索引)

准备关联表
sql 复制代码
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(关联字段无索引)
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;
优化方案:关联字段加索引
sql 复制代码
-- 给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性能差)
sql 复制代码
-- 查询没有下过订单的用户
SELECT * FROM user_info u 
WHERE u.user_id NOT IN (SELECT user_id FROM order_info);
优化方案:使用EXISTS
sql 复制代码
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. 慢查询日志(开启方法)

bash 复制代码
# 临时开启(重启失效)
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. 索引优化建议

sql 复制代码
-- 查看索引使用情况
SHOW INDEX FROM order_info;

-- 分析表(统计数据更新)
ANALYZE TABLE order_info;

-- 查看未使用的索引(MySQL 8.0+)
SELECT * FROM sys.schema_unused_indexes;

五、避坑指南

  1. 不要给所有字段加索引:索引会增加写入开销(INSERT/UPDATE/DELETE)
  2. 避免使用%开头的模糊查询:LIKE '%abc' 会失效索引,LIKE 'abc%' 可使用索引
  3. 避免在索引字段上做函数操作: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'
  4. 避免大事务:长事务会锁表,影响并发性能

总结

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

关键优化口诀

  • 索引优先,覆盖最佳;
  • 避免全扫,排序靠索引;
  • 精准查询,少用*号;
  • 先看执行计划,再动手优化。
相关推荐
二十雨辰5 分钟前
[Java]-Spring面试题
java·开发语言
老马历写记14 分钟前
Maven POM 依赖管理总结
java·maven·system·pom·optional
古法安卓19 分钟前
Android-车机 GNSS 定位数据接收问题排查
android·java·android studio
蔬菜_26 分钟前
前端转全栈-day5(数组、list、set)
java·前端·数据结构·list
萧瑟余晖38 分钟前
Java深入解析篇二十二之虚拟线程
java·开发语言
OuO-241 分钟前
笔试强训 Day 34:ISBN 号码、kotori 和迷宫、矩阵最长递增路径
java·算法·矩阵
zt1985q1 小时前
本地部署开源网络书签与内容管理工具 Karakeep 并实现外部访问
运维·服务器·网络·数据库·网络协议·开源
油丶酸萝卜别吃1 小时前
Java 集合类全景介绍
java·开发语言
钱栈up1 小时前
"Flowable 工作流引擎进阶实战(高级篇):任务分配、流程变量与监听器"
java
豆沙沙包?1 小时前
c++中引用(P7-P11)
java·c++·算法