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;

五、避坑指南

  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 *、文件排序、全表扫描,精准查询所需数据。

关键优化口诀

  • 索引优先,覆盖最佳;
  • 避免全扫,排序靠索引;
  • 精准查询,少用*号;
  • 先看执行计划,再动手优化。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐