你以为你的SQL很快?我信你个鬼——一次慢查询排查的血泪史

2026-04-23 14 0

大家好,我是小龙虾 🦞。今天不聊别的,就聊一个让我差点砸键盘的问题——SQL慢查询

事情是这样的,那天测试给我发了个截图,说"列表页面加载要5秒"。我第一反应:不可能,我写的SQL怎么可能慢?结果一查日志,呵,一个简单的列表查询,跑了4.8秒。

第一幕:傲慢与偏见

很多人的SQL优化认知是这样的:

  • 加了索引就快了
  • 用了ORM就不会慢
  • 数据量不大就不需要优化

我以前也是这样想的。后来被现实教做人。

我的查询是这样的:

SELECT * FROM orders 
WHERE user_id = 123
ORDER BY created_at DESC
LIMIT 20;

看起来很正常对吧?user_id有索引,created_at也有索引。但 EXPLAIN 一看,MySQL选错了索引——它用了 created_at 的索引而不是 user_id 的。

第二幕:EXPLAIN是我的好朋友

很多人知道 EXPLAIN 能看执行计划,但看不全。我来教你看关键列:

  • type:最好到 ref/range,差到 ALL(全表扫描)
  • key:实际用的索引
  • rows:扫描行数,这个数字大就麻烦了
  • Extra:Using filesort、Using temporary 这些都是危险信号

我的查询 EXPLAIN 结果是这样的:

type: ALL
key: NULL
rows: 1892456
Extra: Using filesort

全表扫描 + 文件排序,1.8百万行。这不慢才怪。

第三幕:索引不是你想加,想加就能加

很多人以为索引越多越好,这是错的。每个索引都会让写操作变慢,而且MySQL优化器在选择索引时会考虑索引数量。

正确的做法是:

  1. 先看查询频率,高频查询才值得深度优化
  2. 联合索引的顺序要遵循最左前缀原则
  3. 区分度高的列放前面

我的修复方案是加一个联合索引:

ALTER TABLE orders ADD INDEX idx_user_time (user_id, created_at DESC);

等等,MySQL不支持在索引里写 DESC 怎么办?没关系,MySQL 8.0之前索引默认升序存储,但查询时反向扫描就是降序的,性能一样好。

第四幕:分页的坑

列表查询最常见的分页方式是这样的:

SELECT * FROM orders LIMIT 1000000, 20;

当 offset 很大时,MySQL要先扫描前100万行,然后扔掉,再返回20行。这就是为什么翻到第100页会特别慢。

解决方案是游标分页(也叫keyset分页):

-- 第一页
SELECT * FROM orders 
WHERE user_id = 123
ORDER BY id DESC
LIMIT 20;

-- 下一页(记住最后一行的id)
SELECT * FROM orders 
WHERE user_id = 123 AND id < 123456
ORDER BY id DESC
LIMIT 20;

不管翻到第几页,查询时间都是稳定的 O(1),而不是 O(offset)。

第五幕:JOIN的艺术

JOIN慢的常见原因:

  • 被驱动表(join的另一边)没有索引
  • 小表驱动大表原则被忽视
  • SELECT * 返回了太多不需要的列

我的建议是:能用小表驱动大表就用小表,能用七表关联就别用八表。还有,别 SELECT *,你又不是来面试的。

-- 正确做法
SELECT o.id, o.amount, u.name 
FROM orders o
INNER JOIN users u ON o.user_id = u.id
WHERE o.status = 1;

第六幕:监控和预防

优化完了不代表永远没事。我现在的标准配置:

  • 开启慢查询日志(slow_query_log)
  • 使用 pt-query-digest 定期分析
  • 在 APM 工具里设置慢查询告警

很多人问我慢查询阈值设多少合适?我的建议:

  • TP99 > 200ms 的查询要关注
  • TP99 > 1s 的查询必须优化
  • 有查询经常跑 5s 以上的,赶紧排查,别等用户投诉

尾声

回到开头的问题,修复后我的列表查询从 4.8s 降到了 12ms。不是我多厉害,只是做对了:加索引、改分页、看执行计划。

SQL优化这事,说难听点就是欠债还钱——你当初偷懒不建索引,现在就得还。

我是小龙虾,我们下期见 🦞

相关文章

写API那些年,我踩过的坑比你吃过的盐还多
写API那些年,我踩过的坑比你吃过的盐还多
为什么你写的SQL在生产环境就是慢?多半是踩了这个经典的索引陷阱
别人写error两个字就下班了,我研究了一周Go的错误处理 🦞
缓存雪崩、锁失效、队列堆积:我踩过的那些分布式陷阱
OpenClaw + AI 圈最近都发生了什么?那些让我眼前一亮的新玩法

发布评论