当前位置: 首页 > news >正文

从全表扫描到覆盖索引:我是怎么干掉慢查询的

从全表扫描到覆盖索引:我是怎么干掉慢查询的

生产环境一次慢查询拖垮整条业务线,查了三天最后发现问题竟然出在一个JOIN上——这种事我见过太多了。SQL优化不是玄学,它有方法论、有套路、有可复制的路径。今天我就拿一个真实案例,把从发现问题到解决问题的全过程拆开讲透,看完你就能直接上手用。

一、一次真实的慢查询事故

去年我们团队接手了一个电商后台项目,用户反馈"订单列表页加载特别慢"。我登录数据库一看,果然有问题。

这条查询语句大概长这样:

sql

SELECT o.order_id, o.create_time, u.nickname, p.product_name,

oi.quantity, oi.price

FROM orders o

LEFT JOIN users u ON o.user_id = u.user_id

LEFT JOIN order_items oi ON o.order_id = oi.order_id

LEFT JOIN products p ON oi.product_id = p.product_id

WHERE o.create_time >= '2025-01-01'

AND o.status = 'paid'

ORDER BY o.create_time DESC

LIMIT 20;

这条语句在测试环境没什么问题,但到了生产环境,数据量表膨胀到几百万行之后,执行时间直接飙到了8秒以上。用户等不了,投诉不断。

我用 EXPLAIN 看了一下执行计划,结果触目惊心:

id select_type table type possible_keys key key_len ref rows Extra

1 SIMPLE o ALL idx_create_time NULL NULL NULL 1200000 Using where; Using filesort

1 SIMPLE u eq_ref PRIMARY PRIMARY 4 o.user_id 1 NULL

1 SIMPLE oi ref idx_order_id idx_order_id 4 o.order_id 3 NULL

1 SIMPLE p eq_ref PRIMARY PRIMARY 4 oi.product_id 1 NULL

问题一目了然:orders 表走了全表扫描,扫描了120万行数据。type=ALL 意味着没有用到任何索引,Extra 里的 Using filesort 说明连排序都在内存里硬排,没有利用索引的有序性。

二、问题拆解:这条SQL到底错在哪

很多人看到慢查询,第一反应是"加索引"。但加索引之前,得先搞清楚问题的根源。

1、WHERE 条件没命中索引

WHERE o.create_time >= '2025-01-01' AND o.status = 'paid' 这两个条件,create_time 字段有索引,但 status 没有。数据库优化器在选择执行路径时,发现 status 的区分度不高(paid 订单占了总量的60%以上),所以干脆放弃了 idx_create_time,直接全表扫描。

2、复合索引缺失导致优化器做了最差选择

如果 (status, create_time) 上有一个复合索引,优化器大概率会走这个索引。因为 status 是等值条件,create_time 是范围条件,复合索引的最左匹配原则刚好能用上。

3、ORDER BY 无法利用索引

虽然 create_time 上有单列索引,但因为查询走了全表扫描,ORDER BY o.create_time DESC 根本用不上这个索引的有序性,导致额外的 filesort 操作。

4、SELECT 列有不必要的字段

o.order_id, o.create_time, u.nickname, p.product_name, oi.quantity, oi.price 这些字段全部需要,这个倒没什么问题。但如果有大字段(比如 TEXT 类型),就会造成回表开销。

三、优化方案:三步走,从8秒到0.3秒

找到了病根,接下来就是对症下药。

第一步:加复合索引

sql

ALTER TABLE orders ADD INDEX idx_status_create_time (status, create_time);

加完之后再看 EXPLAIN:

id select_type table type possible_keys key key_len ref rows Extra

1 SIMPLE o range idx_status_create_time idx_status_create_time 13 NULL 48000 Using index condition; Using filesort

扫描行数从120万降到了4.8万,已经是质的飞跃。但还有 Using filesort,说明排序还是有问题。

第二步:调整索引顺序,让排序也能用上索引

把复合索引改成 (status, create_time DESC):

sql

ALTER TABLE orders DROP INDEX idx_status_create_time;

ALTER TABLE orders ADD INDEX idx_status_create_time_desc (status, create_time DESC);

再次 EXPLAIN:

id select_type table type possible_keys key key_len ref rows Extra

1 SIMPLE o range idx_status_create_time_desc idx_status_create_time_desc 13 NULL 48000 Using index condition

Using filesort 消失了!因为索引本身就是按 create_time DESC 排序的,数据库可以直接按索引顺序取数据,不需要额外排序。

第三步:用覆盖索引减少回表

观察一下 SELECT 的字段,order_id、create_time、status 都在索引里,但 user_id 不在。而 JOIN users 需要 user_id。如果把 user_id 也加进索引:

sql

ALTER TABLE orders DROP INDEX idx_status_create_time_desc;

ALTER TABLE orders ADD INDEX idx_status_time_user (status, create_time, user_id);

优化后的执行计划:

id select_type table type possible_keys key key_len ref rows Extra

1 SIMPLE o range idx_status_time_user idx_status_time_user 17 NULL 48000 Using index condition

执行时间从8秒降到了0.3秒。从全表扫描120万行,到索引范围扫描4.8万行,再到利用覆盖索引避免回表,每一步都有明确的收益。

四、几个容易踩的坑

优化SQL这件事,不是加个索引就完了。有几个坑我必须提一下:

1、索引不是越多越好

每加一个索引,INSERT、UPDATE、DELETE 都要维护这个索引,写操作的开销会增加。我见过一个项目,一张表加了十几个索引,结果查询快了,但写入慢了三倍。索引要加在刀刃上,优先考虑 WHERE、JOIN、ORDER BY 涉及的字段。

2、区分度低的字段别单独建索引

比如 status 字段,如果只有 paid、unpaid、refund 三个值,区分度极低。单独给 status 建索引,优化器大概率不会用。这种字段适合放在复合索引的最左边,配合高区分度字段一起用。

3、LIKE '%xxx%' 几乎无法用索引

左模糊查询会导致索引失效,这是硬伤。如果业务上必须支持模糊搜索,建议走 Elasticsearch,别硬扛。

4、函数操作会让索引失效

sql

-- 索引失效

WHERE YEAR(create_time) = 2025

-- 走索引

WHERE create_time >= '2025-01-01' AND create_time < '2026-01-01'

把函数从字段上移到等号右边,这个小改动能让索引从失效变成生效。

5、EXPLAIN 不是看一次就够的

很多人优化完看一眼 EXPLAIN 就觉得完事了。但数据量变化之后,执行计划可能完全不同。我的习惯是在上线前、上线后、数据量翻倍后各看一次 EXPLAIN,确保优化效果在不同数据规模下都稳定。

五、总结:SQL优化的核心思维

回过头来看,这次优化的本质是什么?不是某个神奇的技巧,而是三个基本动作:

1、让 WHERE 条件能命中索引(复合索引 + 最左匹配)

2、让 ORDER BY 能利用索引有序性(索引列顺序与排序方向一致)

3、让 SELECT 尽量走覆盖索引(减少回表)

这三条做到了,大部分慢查询都能解决。剩下的那些极端情况,再考虑 EXPLAIN 分析、执行计划劫持、SQL重写等进阶手段。

SQL优化这件事,说难也难,说简单也简单。难的是没有标准答案,每条SQL的数据分布、业务场景都不一样。简单的是,方法论就那些,EXPLAIN 一跑,问题基本就藏不住了。

别迷信"银弹",也别一上来就加索引。先看执行计划,再动手改,改完再验证。这个习惯养成了,你会发现大部分性能问题都能在半小时内定位并解决。

💡注意:本文所介绍的软件及功能均基于公开信息整理,仅供用户参考。在使用任何软件时,请务必遵守相关法律法规及软件使用协议。同时,本文不涉及任何商业推广或引流行为,仅为用户提供一个了解和使用该工具的渠道。

你在生活中时遇到了哪些问题?你是如何解决的?欢迎在评论区分享你的经验和心得!

希望这篇文章能够满足您的需求,如果您有任何修改意见或需要进一步的帮助,请随时告诉我!

感谢各位支持,可以关注我的个人主页,找到你所需要的宝贝。

博文入口:山峰哥-CSDN博客复制到【浏览器】打开即可,宝贝入口:常用软件宝贝:精品文件

作者郑重声明,本文内容为本人原创文章,纯净无利益纠葛,如有不妥之处,请及时联系修改或删除。诚邀各位读者秉持理性态度交流,共筑和谐讨论氛围~

http://www.jsqmd.com/news/1009362/

相关文章:

  • IEEE论文投稿前必查:作者单位排版里的5个隐形坑(LaTeX避坑指南)
  • 2026年热门的自动化配电箱/定制配电箱公司对比推荐 - 品牌宣传支持者
  • 从“分流器”到“电流检测电阻”:这个小元件的前世今生与选型实战
  • LFW数据集之外:聊聊人脸识别算法评测的那些‘坑’与真实场景挑战
  • 别再只盯着RAID了!聊聊JBOD在冷数据归档和视频制作里的那些‘实在’用法
  • 深入解析NXP Kinetis SIM模块:从HAL抽象到时钟与信号路由实战
  • 告别显存焦虑:用AWQ和GPTQ在消费级显卡上跑大模型的保姆级教程
  • PyQt5界面代码维护指南:.ui文件 vs 纯Python代码,哪种方式更适合你的项目?
  • 2026年口碑好的贵州泡沫混凝土工程/屋面泡沫混凝土优质厂家推荐榜 - 行业平台推荐
  • 2026q2河北折叠门厂家评测:唐山,邢台,廊坊,河北抗爆窗/河北抗爆门/河北泄压门/资质性能与服务对比 - 优质品牌商家
  • 从 .netrc 到 OAuth2:聊聊那些年我们用过的命令行认证方式(以 Go 项目为例)
  • nnDetection vs. nnU-Net:医学图像分割和检测,我到底该选哪个?
  • 从一次线上故障说起:复盘我们如何用MaxScale替换ProxySQL,解决了查询缓存带来的数据延迟问题
  • 别再只盯着FOC了!聊聊永磁电机那些‘基本功’:V/F、DTC和矢量控制到底怎么选?
  • 2026年评价高的污泥压滤机/洗煤压滤机/选矿精矿压滤机/山东压滤机厂家推荐与选型指南 - 行业平台推荐
  • APK安装器架构解析:Windows平台安卓应用部署的技术实现与实践
  • 从SGD到Adam:图解十大优化算法原理,5分钟搞懂各自适用场景
  • 5个常见问题解决指南:Windows版Mesa3D图形驱动安装与故障排除
  • 2026年靠谱的市政排水管/贵州顶管可靠供应商推荐 - 品牌宣传支持者
  • 从T60/T25P发布,聊聊新手入行植保飞手:考证、接单、设备选择全攻略
  • 别再只盯着MEMS了!聊聊谐振式加速度计:从石英晶体到高精度导航,它到底强在哪?
  • 2026年新型3D打印代加工服务商综合能力观察:从技术落地到交付保障的行业分析 - 优质品牌商家
  • 2026年北京刑事辩护律师避坑指南:5位经验丰富实力派推荐 - 本地品牌推荐
  • 从‘盲人下山’到‘智能导航’:用生活化比喻秒懂深度学习优化器(SGD/动量/Adagrad/RMSProp/Adam)
  • 2026年靠谱的广东PZ30配电箱/广东低压配电箱/配电箱批量采购厂家推荐 - 行业平台推荐
  • 2026年家庭打深水井怎么选?苏州航通、成都易申、文安源翔综合能力对比评测 - 优质品牌商家
  • 别再傻傻分不清!ULPI、UTMI+、HSIC三种USB PHY接口,硬件工程师选型避坑指南
  • VBA选型之争:Dictionary与Collection,性能差竟达8倍
  • 2026年更新云浮电子回收电话:行业趋势与服务商深度解析 - 品牌鉴赏官2026
  • 从跑酷到搬砖:拆解波士顿动力Atlas机器人背后的液压驱动与电机驱动之争