这次分享,是我在项目中亲身经历的一个"接口变慢"的真实案例。
有一天业务方突然反馈,说有个订单查询接口"变得很慢",用户点一下,几秒钟才出数据。起初我们以为是 Redis 缓存没命中,或者服务器资源吃满了,结果查了一圈,问题出在------SQL执行计划选错索引了。
这次优化经历很有意思,也很有代表性,我想整理下来分享给大家。
1️⃣ 线上接口为什么慢?
我们的问题出在一个分页查询接口,SQL 大致如下:
ruby
SELECT * FROM t_orderWHERE status = ?AND create_time BETWEEN ? AND ?ORDER BY create_time DESCLIMIT ?, ?;
表里大概 500 万条数据,字段都有索引,怎么看都挺"正常"。
但是:
- 本地开发环境执行只需 50ms;
- 上线生产后,执行时间飙升到 3.6 秒;
- 分页越靠后越慢,甚至会超时。
这就不正常了。
2️⃣ EXPLAIN 看执行计划,问题找到了
通过 EXPLAIN
一查,执行计划如下:
type
: rangekey
: create_timerows
: 4500000Extra
: Using where; Using filesort
看到这我就懵了:为啥用了 create_time
的索引,而不是 status + create_time
的组合索引?
这意味着:MySQL 扫描的数据太多,效率极低。
3️⃣ 如何优化?
我们分析之后做了两点调整:
✅ 加组合索引:
sql
ALTER TABLE t_order ADD INDEX idx_status_time (status, create_time);
这样能让 MySQL 直接定位到符合 status
的数据段,再通过 create_time
快速定位,提高效率。
✅ 避免深度分页:
Limit 页码越大,MySQL 需要跳过更多记录。可以考虑:
- 改用 游标分页(基于 create_time + id 作为下一页起点);
- 热门页做缓存,比如第一页用 Redis 缓存起来。
4️⃣ 优化效果
我们做完索引调整后,对比数据如下:
分页位置 | 优化前耗时 | 优化后耗时 |
---|---|---|
第 1 页 | 300ms | 50ms |
第 100 页 | 3.6s | 200ms |
效果非常明显,性能提升 10 倍以上。
5️⃣ 小结:关于慢 SQL 的常见坑
- ✅ 模糊查询 like '%xxx',容易导致索引失效;
- ✅ where 里使用函数(如 DATE(create_time)),也会失效;
- ✅ 多字段组合索引,顺序不对等于没用;
- ✅ limit 深分页,慎用;
- ✅ select * 查询大字段,响应也会慢。
写在最后
这次慢 SQL 优化的过程,其实也让我更加理解了一件事:
工具不难,关键在于理解背后的机制。
MySQL 到底用了哪个索引?为什么用了这个索引?这些都是我们必须掌握的技能点。
如果你也遇到过类似问题,欢迎留言聊聊,我们一起进步 👇
📌 后续我还会分享更多数据库相关实战经验:索引设计、锁机制、事务问题、慢日志分析......
欢迎关注「Debug笔记」,咱们下篇见~