跳转至

MySQL 调优实战案例

一、发现问题

慢查询日志开启:

slow_query_log = 1
long_query_time = 1     -- 超过 1 秒记录
slow_query_log_file = /var/log/mysql/slow.log

pt-query-digest 分析慢日志,找到 Top SQL。

二、案例:订单列表查询慢

现象

SELECT * FROM t_order
WHERE user_id = 12345
  AND status = 'PAID'
ORDER BY create_time DESC
LIMIT 20;

数据量 1000 万,执行 3 秒。

EXPLAIN 分析

EXPLAIN SELECT ...

关键看:

含义 关注
type 访问类型 至少 range,最好 ref / const
key 实际用的索引 NULL 表示没用索引
rows 预估扫描行数 越小越好
Extra 额外信息 Using filesort / Using temporary 要警惕

本例结果:

type: ALL
key: NULL
rows: 10000000
Extra: Using where; Using filesort

全表扫描 + filesort。

三、优化过程

1. 加联合索引

ALTER TABLE t_order ADD INDEX idx_user_status_time(user_id, status, create_time);

按 WHERE 等值条件在前,排序字段在后。

2. 再 EXPLAIN

type: ref
key: idx_user_status_time
rows: 200
Extra: Using where

走索引,扫描 200 行,filesort 消失。执行时间从 3s → 20ms。

3. 避免 SELECT *

只查需要的列:

SELECT id, order_no, amount, create_time FROM t_order ...

避免回表(覆盖索引)。

四、其他常见调优点

1. 深分页优化

-- 慢:LIMIT 1000000, 20
SELECT * FROM t_order ORDER BY id LIMIT 1000000, 20;

-- 快:子查询先定位 id
SELECT * FROM t_order
WHERE id >= (SELECT id FROM t_order ORDER BY id LIMIT 1000000, 1)
ORDER BY id LIMIT 20;

2. 避免索引失效

  • WHERE age + 1 = 10WHERE age = 9
  • WHERE DATE(create_time) = '2024-01-01'WHERE create_time >= '2024-01-01' AND create_time < '2024-01-02'
  • LIKE '%abc' 无法走普通索引。

3. join 字段类型一致

a.uid = b.uid,两边类型不一致会导致索引失效。

五、调优方法论

  1. 抓慢 SQL:慢查询日志 / 监控。
  2. EXPLAIN:看 type、key、rows、Extra。
  3. 加索引:WHERE、ORDER BY、GROUP BY、JOIN 字段。
  4. 改写 SQL:避免函数、避免 SELECT *、深分页优化。
  5. 验证:EXPLAIN + 实测执行时间。
  6. 复盘:记录到知识库。

高频追问

  • 什么是覆盖索引?索引包含查询所需所有列,不需要回表。
  • 什么是回表?二级索引查到主键,再去聚簇索引取完整行。
  • 索引不是越多越好:写操作要维护索引,占空间。