【案例】MySQL慢查询优化实战:从10秒到100毫秒


阿里云特惠 - 新用户专享

MySQL慢查询优化实战案例

本文记录一个真实的慢查询优化过程:某电商订单查询从10秒优化到100毫秒,性能提升100倍.

问题背景

运营后台的订单列表查询超时,用户抱怨无法使用:

-- 原始慢查询
SELECT * FROM orders 
WHERE user_id = 12345 
  AND created_at >= '2026-01-01'
  AND status IN ('paid', 'shipped')
ORDER BY created_at DESC
LIMIT 20;

-- 执行时间:10.2秒!

诊断过程

第1步:查看执行计划

EXPLAIN SELECT * FROM orders ...

-- 输出:
-- +----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
-- | id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
-- +----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
-- |  1 | SIMPLE      | orders | ALL  | NULL          | NULL | NULL    | NULL | 523456 | Using where |
-- +----+-------------+--------+------+---------------+------+---------+------+--------+-------------+

-- 关键信息:
-- type=ALL 表示全表扫描!
-- rows=523456 扫描了52万行

第2步:分析表结构

SHOW INDEX FROM orders;
-- 发现只有主键索引,没有其他索引!

SHOW TABLE STATUS LIKE 'orders';
-- 表大小:2.1GB
-- 行数:520万行

优化方案

方案1:添加联合索引(推荐)

-- 分析查询条件:
-- WHERE user_id = ? AND created_at >= ? AND status IN ?
-- ORDER BY created_at DESC

-- 创建联合索引,注意字段顺序
-- 等值查询字段在前,范围查询在后,排序字段最后
ALTER TABLE orders ADD INDEX idx_user_created_status 
(user_id, created_at, status);

-- 验证执行计划
EXPLAIN SELECT * FROM orders ...
-- type=range, key=idx_user_created_status, rows=125
-- 扫描行数从52万降到125行!

方案2:覆盖索引优化(进阶)

-- 如果只需要特定字段,用覆盖索引避免回表
SELECT order_id, user_id, total_amount, status, created_at 
FROM orders 
WHERE user_id = 12345 
  AND created_at >= '2026-01-01'
ORDER BY created_at DESC
LIMIT 20;

-- 创建覆盖索引
ALTER TABLE orders ADD INDEX idx_cover 
(user_id, created_at, status, order_id, total_amount);

-- 执行计划显示:Extra=Using index(覆盖索引)
-- 无需回表查数据,性能进一步提升

优化效果对比

阶段 执行时间 扫描行数 优化手段
优化前 10.2秒 523,456 无索引,全表扫描
加联合索引 0.8秒 125 idx_user_created_status
覆盖索引 0.1秒 125 覆盖索引避免回表

经验总结

  1. 慢查询先查执行计划 – EXPLAIN是优化的起点
  2. 索引字段顺序很重要 – 等值在前,范围在后
  3. 覆盖索引是利器 – 避免回表能大幅提升性能
  4. 定期分析慢查询日志 – 用pt-query-digest找出最耗时的SQL

延伸工具

# 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;  -- 超过1秒的SQL记录

# 分析慢查询
pt-query-digest /var/lib/mysql/slow.log

# 查看索引使用情况
SELECT * FROM sys.schema_unused_indexes 
WHERE object_schema = 'your_db';

总结

数据库优化没有银弹,但遵循”先诊断,后优化”的原则,大多数慢查询都能通过合理的索引设计解决.

发表评论