MySQL慢查询优化实战:从日志分析到SQL重构全流程
⚡ MySQL慢查询优化实战:从日志分析到SQL重构全流程
一篇带你从发现慢查询 → 分析 → 优化 → 测试的完整实战教程
🧩 关键词:MySQL优化 / 性能分析 / 慢查询 / 索引设计
🔍 一、前言
在大型业务系统中,MySQL 慢查询会导致:
- 系统响应变慢
- 页面加载延迟
- 高并发下锁等待严重

解决方案就是 找到慢查询 → 分析执行计划 → 优化 SQL → 设计合理索引。
本文通过一个 电商订单查询系统 的实例,带你全流程实战演示。
🏗 二、业务场景
假设我们有一个电商系统 orders 表:
CREATE TABLE orders (id BIGINT PRIMARY KEY AUTO_INCREMENT,user_id BIGINT NOT NULL,product_id BIGINT NOT NULL,status VARCHAR(20),total_amount DECIMAL(10,2),created_at DATETIME,INDEX idx_user_created (user_id, created_at)
);

业务需求:
- 查询某用户在某时间段的订单
- 查询某状态订单总额
- 高峰期每天有百万级订单
🧾 三、开启慢查询日志
修改 MySQL 配置文件 my.cnf:
[mysqld]
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1 # 秒
log_queries_not_using_indexes = 1
重启 MySQL:
sudo systemctl restart mysql
查询慢查询日志示例:
# Time: 2025-11-05T15:00:01.000000Z
# User@Host: root[root] @ localhost []
# Query_time: 3.542 Lock_time: 0.000 Rows_sent: 500 Rows_examined: 100000
SELECT * FROM orders WHERE user_id=12345 AND created_at BETWEEN '2025-11-01' AND '2025-11-05';
注意:Rows_examined=100,000 → 查询扫描了大量行,效率低
🧐 四、执行计划分析
使用 EXPLAIN 分析 SQL:
EXPLAIN SELECT * FROM orders
WHERE user_id=12345 AND created_at BETWEEN '2025-11-01' AND '2025-11-05';
输出示例:
| id | select_type | table | type | possible_keys | key | rows | Extra |
|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | orders | ref | idx_user_created | idx_user_created | 100000 | Using where |
分析:
- 查询使用了
idx_user_created索引,但扫描行数仍然多 - 原因:索引顺序和条件不完全匹配
🛠 五、SQL重构与索引优化
1️⃣ 优化 SQL
原 SQL:
SELECT * FROM orders
WHERE user_id=12345 AND created_at BETWEEN '2025-11-01' AND '2025-11-05';
优化 SQL:
SELECT id, product_id, total_amount
FROM orders
WHERE user_id=12345 AND created_at >= '2025-11-01' AND created_at <= '2025-11-05';
✅ 优化点:
- 只查询必要字段,避免
SELECT * - 避免不必要的数据扫描
2️⃣ 调整索引
当前索引:
INDEX idx_user_created (user_id, created_at)
优化方案:
- 如果查询经常按
status和时间筛选,可加复合索引:
ALTER TABLE orders
ADD INDEX idx_user_status_created (user_id, status, created_at);
- 这样查询
WHERE user_id=12345 AND status='PAID' AND created_at BETWEEN ...可以直接走索引覆盖
3️⃣ 验证优化效果
执行优化后的 SQL:
EXPLAIN SELECT id, product_id, total_amount
FROM orders
WHERE user_id=12345 AND status='PAID' AND created_at BETWEEN '2025-11-01' AND '2025-11-05';
输出示例:
| id | select_type | table | type | possible_keys | key | rows | Extra |
|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | orders | ref | idx_user_status_created | idx_user_status_created | 500 | Using index |
✅ Rows 从 100,000 降到 500,查询速度提升近 200 倍
🔁 六、慢查询优化全流程总结
-
发现慢查询
- 慢查询日志 +
long_query_time pt-query-digest分析大量慢查询
- 慢查询日志 +
-
分析执行计划
EXPLAIN查看扫描方式、索引使用情况- 关注
rows、Extra
-
SQL优化
- 避免
SELECT * - 使用必要字段
- 合理拆分复杂查询
- 避免
-
索引优化
- 复合索引覆盖查询条件
- 使用前缀索引或联合索引减少扫描量
- 删除冗余索引
-
验证与回归测试
- EXPLAIN + 实测响应时间
- 高并发下压测
📊 七、日志分析工具推荐
mysqldumpslow- Percona Toolkit:
pt-query-digest - Grafana + Prometheus + MySQL Exporter
- 慢查询日志可视化面板,辅助优化
!实战范例: MySQL慢查询优化实战项目演示:百万订单表优化
1️⃣ 数据准备:生成百万级订单表
CREATE DATABASE IF NOT EXISTS shop;
USE shop;DROP TABLE IF EXISTS orders;
CREATE TABLE orders (id BIGINT PRIMARY KEY AUTO_INCREMENT,user_id BIGINT NOT NULL,product_id BIGINT NOT NULL,status VARCHAR(20),total_amount DECIMAL(10,2),created_at DATETIME,INDEX idx_user_created (user_id, created_at)
);-- 插入测试数据(模拟100万条)
DELIMITER $$CREATE PROCEDURE populate_orders()
BEGINDECLARE i INT DEFAULT 1;WHILE i <= 1000000 DOINSERT INTO orders(user_id, product_id, status, total_amount, created_at)VALUES(FLOOR(1 + RAND() * 10000), -- user_idFLOOR(1 + RAND() * 500), -- product_idELT(FLOOR(1 + RAND()*3), 'PAID','PENDING','CANCELLED'),ROUND(RAND()*1000, 2),NOW() - INTERVAL FLOOR(RAND()*30) DAY);SET i = i + 1;END WHILE;
END$$DELIMITER ;CALL populate_orders();
✅ 这样我们就有了百万级订单表,可真实模拟慢查询。
2️⃣ 原始慢查询测试
原 SQL 示例(经常使用场景):
SELECT *
FROM orders
WHERE user_id=1234 AND created_at BETWEEN '2025-11-01' AND '2025-11-05';
执行前加 EXPLAIN 分析:
EXPLAIN SELECT *
FROM orders
WHERE user_id=1234 AND created_at BETWEEN '2025-11-01' AND '2025-11-05';
输出示例:
| id | table | type | possible_keys | key | rows | Extra |
|---|---|---|---|---|---|---|
| 1 | orders | ref | idx_user_created | idx_user_created | 100000 | Using where |
Rows=100,000 → 仍然扫描大量行,效率低
3️⃣ SQL重构与索引优化
优化 SQL
- 只查询需要字段
- 避免
SELECT *
SELECT id, product_id, total_amount
FROM orders
WHERE user_id=1234 AND created_at BETWEEN '2025-11-01' AND '2025-11-05';
新增复合索引
ALTER TABLE orders
ADD INDEX idx_user_created_status (user_id, status, created_at);
4️⃣ 优化后性能验证
EXPLAIN SELECT id, product_id, total_amount
FROM orders
WHERE user_id=1234 AND status='PAID'AND created_at BETWEEN '2025-11-01' AND '2025-11-05';
| id | table | type | key | rows | Extra |
|---|---|---|---|---|---|
| 1 | orders | ref | idx_user_created_status | 500 | Using index |
✅ Rows 从 100,000 → 500,查询速度提升近 200倍。
5️⃣ Node.js 测试脚本(可选)
用 Node.js 模拟实际请求,测试查询性能:
const mysql = require('mysql2/promise');(async function() {const conn = await mysql.createConnection({host: 'localhost',user: 'root',password: '123456',database: 'shop'});console.time('原SQL');await conn.query(`SELECT * FROM orders WHERE user_id=1234 AND created_at BETWEEN '2025-11-01' AND '2025-11-05'`);console.timeEnd('原SQL');console.time('优化SQL');await conn.query(`SELECT id, product_id, total_amount FROM orders WHERE user_id=1234 AND status='PAID' AND created_at BETWEEN '2025-11-01' AND '2025-11-05'`);console.timeEnd('优化SQL');await conn.end();
})();
运行结果示例:
原SQL: 2450ms
优化SQL: 12ms
6️⃣ 总结
通过这个实战项目,我们完成了:
- 百万级订单表数据生成
- 原始 SQL 慢查询分析
- SQL 重构 + 索引优化
- 查询性能测试验证
✅ 实战结果:
- 原 SQL:Rows 扫描 10万+,耗时 2-3秒
- 优化 SQL:Rows 扫描 500,耗时 10-15ms
- 高并发场景下性能显著提升
