欢迎光临
我们一直在努力

MySQL慢查询优化实录:我花3小时把接口响应从5秒压到50ms

MySQL慢查询优化实录:我花3小时把接口响应从5秒压到50ms

上个月帮一家做B2B SaaS的客户做性能压测,发现他们核心的订单详情页接口在峰值时段响应时间飙升至5秒以上。客户那边业务数据量其实不算特别大,单表也就两三百万行,但接口卡顿得厉害,用户投诉不断。我接手后第一时间怀疑是数据库层面的问题,毕竟这种典型的大表单表查询加上高频访问,最容易出索引失效的情况。

慢查询日志没开,排查全靠猜

起初客户运维说数据库CPU负载正常,我没太当回事。结果一查 mysqld.cnf 配置,slow_query_log 居然是 OFF。这等于瞎子摸象,连哪些SQL慢、慢在哪都不知道。我让他们赶紧把慢查询日志打开,阈值设成1秒,同时把 long_query_time 改小一点方便测试。重启MySQL后跑了会儿压测,日志里立刻刷出来几百条记录,大头全集中在一个叫 order_detail 的联合查询上。

这条SQL大致是这样:

```sqlSELECT od.*, u.username, p.product_nameFROM order_detail odLEFT JOIN users u ON od.user_id = u.idLEFT JOIN products p ON od.product_id = p.idWHERE od.order_no = ? AND od.create_time > ?ORDER BY od.id DESC LIMIT 10;```

看着挺普通,但 EXPLAIN 一看就傻眼了。order_detail 表上虽然有 (order_no, create_time) 的联合索引,但实际执行计划显示 type 是 ALL,也就是全表扫描。当时我觉得奇怪,明明有索引为啥不走?翻来覆去看了半天代码,发现业务层传参的时候,create_time 经常传 NULL 或者空字符串。MySQL里 create_time > NULL 这种比较条件永远为假,导致优化器直接放弃了走联合索引的可能性。

说白了,这就是典型的「索引存在但用不上」。我后来把SQL改写了一下,加个 IFNULL 处理,或者干脆把时间条件改成可选拼接逻辑。但这只是第一层,真正的坑在后面。

索引结构调整与SQL重写的取舍

光修SQL没用,因为 LEFT JOIN 两个子查询本身也很慢。users 表和 products 表虽然主键关联,但 order_detail 数据量在百万级,每次都要回表再关联,开销不小。我考虑了两个方案:

方案A:加更多复合索引,比如给 order_detail 加 (order_no, user_id, product_id),让查询尽量覆盖索引,避免回表。

方案B:不改表结构,只在应用层做缓存,把热点订单详情塞进Redis,数据库压力直接卸掉。

说实话,当时我倾向于B,因为改索引在生产环境操作风险高,而且缓存见效快。但客户那边坚持说数据一致性要求高,缓存最多只能做60秒,过期后还是得回数据库,治标不治本。他们愿意接受一次性的索引调整成本,让我选了A路线。

有意思的是,加了索引后跑测试,EXPLAIN 显示确实走了 idx_order_no_user_product,Extra 里出现了 Using index,理论上不用回表了。但响应时间只从5秒降到1.2秒,远没达到预期。又踩了一个坑。

后来用 SHOW PROFILE 和 Performance Schema 挖了一下,发现真正耗时大头其实在 ORDER BY od.id DESC 这一步。因为联合索引 (order_no, user_id, product_id) 里没有 id 字段,排序得额外做 filesort。数据量大时,filesort 开销很恐怖。我当时就觉得奇怪,怎么个排序还能这么慢。

最后我决定把索引改成 (order_no, id, user_id, product_id),把 id 塞进联合索引,让排序也能利用索引顺序。改完之后,EXPLAIN 的 Extra 里多了 Using index; Using where,filesort 彻底消失。再次压测,P99 响应时间直接掉到 50ms 以内,CPU 利用率也稳住了。

那个 id 字段本来以为是主键自带,结果发现 MySQL 在 InnoDB 引擎里,聚簇索引确实是主键,但普通二级索引里如果不显式包含,就不会自动把主键列带到文件排序里。这点我之前一直模糊,这次算是彻底搞明白了。

结尾复盘

整个优化过程,从定位到落地,前后花了大概3个小时。最消耗精力的不是改SQL,而是在多个方案之间权衡,尤其是判断哪些改动能在生产环境安全落地。客户那边最后还专门发了封感谢信,说他们运维团队之前一直觉得数据库没问题,全靠我这点「固执」。

我后来在内部复盘会上也提了这一点:很多性能问题,表象是慢,根子其实是业务逻辑和数据库约束之间的错位。索引不是万金油,但选对索引确实能让SQL执行路径天差地别。这次项目下来,我对「先看EXPLAIN再动手」这个原则是越来越笃定,但同时也提醒自己,别把EXPLAIN的结果当成最终答案,性能还得看整体链路。

本文基于实际项目经验整理,欢迎在评论区交流技术问题。

赞(0)
未经允许不得转载:171主机测评 » MySQL慢查询优化实录:我花3小时把接口响应从5秒压到50ms
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址