一条SQL从3秒优化到45毫秒,我只做了三步

📅 2026/7/11 2:46:23
一条SQL从3秒优化到45毫秒,我只做了三步
一条SQL从3秒优化到45毫秒我只做了三步凌晨两点被告警电话吵醒打开监控一看订单列表接口直接超时了。排查了半天最后发现就是一条SQL在作妖——全表扫描了500多万行。这种事每个做后端的人都碰到过但真正能一步步把问题解决干净的其实没那么多。今天我把这次优化的完整过程掰开了讲从发现问题到最终落地每一步都有真实数据希望能帮你少踩几个坑。一、问题浮现一个接口拖垮了整个后台我们的电商平台后台有个订单管理页面功能不复杂——支持按订单状态、下单时间范围筛选然后按创建时间倒序展示最新20条记录还得带上用户昵称。上线半年后订单数据量从几万涨到了500多万条这个接口的响应时间从最初的180ms一路飙到3秒以上高峰期直接超时。先看看原始SQL长什么样sqlSELECTo.id, o.order_no, o.user_id, o.total_amount,o.status, o.created_at, u.nicknameFROM orders oLEFT JOIN users u ON o.user_id u.idWHERE o.status IN (1, 2, 3)AND o.created_at BETWEEN 2025-01-01 AND 2026-06-22ORDER BY o.created_at DESCLIMIT 20 OFFSET 0;这条SQL乍一看挺正常的但执行计划一拉出来问题全暴露了。二、EXPLAIN对比优化前后差距有多大优化之前EXPLAIN的结果如下字段 优化前 说明type ALL 全表扫描最差的访问方式possible_keys NULL 没有可用索引key NULL 实际未使用任何索引rows 4876320 扫描了将近500万行Extra Using filesort 需要额外的文件排序几个关键问题非常明确1、orders表虽然建了user_id和created_at的单列索引但查询条件是status IN (1,2,3)配合created_at BETWEEN ...这两个条件的组合没有对应的联合索引优化器直接放弃索引选了全表扫描。2、ORDER BY created_at DESC触发了Using filesort意味着MySQL需要把所有符合条件的行加载到内存中再排序500万行的排序开销可想而知。3、LEFT JOIN users虽然走的是主键关联但因为驱动表扫描了近500万行JOIN的累计开销也被大幅放大。到这里优化方向基本清晰了——核心就是两件事让索引生效让排序不走文件排序。三、第一轮优化建联合索引让查询走索引扫描最直接的思路就是给orders表加一个联合索引把status和created_at都包含进去。这里有个关键点根据MySQL的最左前缀原则等值查询的列应该放在前面范围查询的列放在后面。所以索引顺序是status在前created_at在后。sqlALTER TABLE orders ADD INDEX idx_status_created (status, created_at);加完索引后再次执行EXPLAIN字段 优化后 变化type range 从全表扫描变为范围扫描possible_keys idx_status_created 出现了可用索引key idx_status_created 实际用上了索引rows 186542 扫描行数从500万降到18万Extra Using index condition 有改善但仍有filesort响应时间从3秒多降到了800ms左右进步明显。但ORDER BY的问题依然存在Using filesort还在说明索引的顺序和排序方向不匹配。四、第二轮优化覆盖索引加降序彻底干掉文件排序问题的根子在于我们建的索引(status, created_at)默认是升序排列的而查询要求ORDER BY created_at DESC。虽然MySQL 8.0支持索引降序扫描但为了兼容性和确定性最稳妥的做法是把索引改成降序同时把查询需要的字段也塞进索引里做成覆盖索引避免回表。具体操作如下sqlALTER TABLE orders DROP INDEX idx_status_created;ALTER TABLE orders ADD INDEX idx_status_created_cover (status,created_at DESC,id,order_no,total_amount);同时SQL也做了调整先把JOIN去掉只查必要字段sqlSELECTo.id, o.order_no, o.user_id, o.total_amount,o.status, o.created_atFROM orders oWHERE o.status IN (1, 2, 3)AND o.created_at BETWEEN 2025-01-01 AND 2026-06-22ORDER BY o.created_at DESCLIMIT 20 OFFSET 0;再次执行EXPLAIN字段 优化后 说明type range 保持范围扫描key idx_status_created_cover 使用覆盖索引rows 98234 扫描行数进一步下降Extra Using index 覆盖索引无需回表Extra 无filesort 排序问题彻底解决响应时间直接降到了45ms。从3秒到45毫秒提升了将近70倍。这个结果让整个团队都松了口气。五、第三轮优化延迟关联把JOIN的开销压到最低上面我们把JOIN去掉了但实际业务中用户昵称确实需要展示。如果直接LEFT JOIN虽然users.id是主键关联很快但orders表扫描的行数仍然有近10万每一行都去users表查一次累计开销也不小。这里用一个经典技巧——延迟关联。核心思路是先在子查询里只查orders表利用覆盖索引拿到20条记录的主键ID再用这20个ID去关联users表。sqlSELECTo.id, o.order_no, o.user_id, o.total_amount,o.status, o.created_at, u.nicknameFROM (SELECT id, order_no, user_id, total_amount, status, created_atFROM ordersWHERE status IN (1, 2, 3)AND created_at BETWEEN 2025-01-01 AND 2026-06-22ORDER BY created_at DESCLIMIT 20 OFFSET 0) oLEFT JOIN users u ON o.user_id u.id;这样做的好处非常明显子查询走覆盖索引只需要扫描20行JOIN操作也只执行20次而不是10万次。最终响应时间稳定在50ms以内完全满足业务SLA要求。六、几个实战中容易踩的坑1、深分页问题当OFFSET很大时比如OFFSET 100000MySQL依然需要扫描前10万行再丢弃。这种场景建议用游标分页替代也就是WHERE id 上一页最大ID避免大OFFSET带来的性能损耗。举个例子sql-- 不推荐SELECT * FROM orders ORDER BY created_at DESC LIMIT 20 OFFSET 100000;-- 推荐SELECT * FROM ordersWHERE id (SELECT id FROM orders ORDER BY created_at DESC LIMIT 100000, 1)ORDER BY created_at DESC LIMIT 20;2、IN条件元素过多IN (1,2,3)完全没问题但如果IN里面塞了几十上百个值优化器可能会放弃索引走全表扫描。这种情况建议改成JOIN一张临时表或者拆成多个UNION ALL。3、索引不是越多越好每多一个索引INSERT、UPDATE、DELETE都会变慢因为索引也需要维护。生产环境加索引之前一定要评估写入频率别为了一个查询把写入性能搞崩了。我们当时加这个联合索引之后订单创建接口的耗时从12ms涨到了18ms虽然有影响但在可接受范围内。4、EXPLAIN的rows只是估算值EXPLAIN给出的扫描行数是优化器的估算和实际执行可能有偏差。真正靠谱的判断依据还是慢查询日志和实际响应时间EXPLAIN只是帮你缩小排查范围的工具别把它当最终结论。5、不要迷信工具推荐的索引有些ORM框架或者云数据库控制台会自动推荐索引但它们不了解你的业务查询模式。最终还是得自己根据实际SQL和EXPLAIN结果来判断工具给的建议只能当参考。七、写在最后回头看这次优化核心就三步用EXPLAIN定位瓶颈根据查询条件建合适的联合索引用覆盖索引和延迟关联把回表和JOIN的开销压到最低。SQL优化这事没有银弹但有方法。大多数慢查询的根源都是索引没用对把这一关过了80%的性能问题都能解决。剩下那20%才轮到SQL改写、分库分表这些大招上场。希望这篇实战记录对你有帮助少走点弯路。注意本文所介绍的软件及功能均基于公开信息整理仅供用户参考。在使用任何软件时请务必遵守相关法律法规及软件使用协议。同时本文不涉及任何商业推广或引流行为仅为用户提供一个了解和使用该工具的渠道。你在生活中时遇到了哪些问题你是如何解决的欢迎在评论区分享你的经验和心得希望这篇文章能够满足您的需求如果您有任何修改意见或需要进一步的帮助请随时告诉我感谢各位支持可以关注我的个人主页找到你所需要的宝贝。博文入口山峰哥-CSDN博客复制到【浏览器】打开即可,宝贝入口常用软件宝贝精品文件作者郑重声明本文内容为本人原创文章纯净无利益纠葛如有不妥之处请及时联系修改或删除。诚邀各位读者秉持理性态度交流共筑和谐讨论氛围发现夸克网盘链接×是否打开打开No关闭