慢查询不一定是索引不够:数据库索引失效排查与执行计划读法实战
一张表明明建了索引,查询还是慢,很多人第一反应是再加一个索引。实际排查中,真正的原因往往不是索引不够,而是已有的索引没有被用上,或者被用错了地方。本文用一个订单表的例子,讲清索引失效的常见原因、执行计划的读法,以及一套可以照着做的排查清单。
一、先别加索引,先看执行计划
慢查询出现后,先不要凭感觉加索引。先用执行计划看数据库到底走了哪条路。重点看三个信息:第一是访问方式,是全表扫描还是索引扫描;第二是实际扫描的行数,扫描行数远大于返回行数,说明过滤发生在扫描之后;第三是是否出现了文件排序或临时表,这通常意味着排序字段没有合适的索引支撑。
一个常见的误区是只看查询耗时。耗时受缓存、并发和数据量影响,同一条语句在不同时间可能差别很大。执行计划才是相对稳定的证据。把慢查询的执行计划保存下来,再改索引、再对比一次,才能确认优化是否真的生效。
二、索引失效的六个常见原因
第一,对索引列做函数或运算。把日期字段用函数截断、把数值字段做加减运算,数据库往往无法直接利用原索引,因为它需要先算出每一行的结果再比较。能改写查询就改写查询,让索引列保持原样出现在比较的一侧。
第二,隐式类型转换。字段是字符串,查询却传了数字,或者反过来,数据库可能把字段一侧做了转换,结果索引失效。保持查询参数类型与字段类型一致,是最容易被忽视、也最容易修复的一类问题。
第三,前导列缺失。复合索引讲究最左前缀,先建了用户编号加订单时间的复合索引,查询却只按订单时间过滤,前导列没有约束,索引的定位能力就会大打折扣。建索引之前,先列出高频查询的过滤条件组合,再决定列的顺序。
第四,范围条件截断后面的列。复合索引中,第一个范围条件之后的列通常难以继续精确定位。如果范围列在前、等值列在后,可以考虑调整顺序,或者把范围查询拆开,先用等值条件缩小范围。
第五,选择性太低。性别、状态这类只有少数取值的字段,单独建索引往往收益有限,扫描索引再回表的成本可能接近全表扫描。这类字段更适合与高选择性字段组合,或者直接用分区、归档等手段缩小数据规模。
第六,数据倾斜与统计信息过期。表里大部分订单都是已完成状态,少数是待处理状态,优化器如果拿着过期的统计信息做判断,就可能选错路径。定期更新统计信息、留意数据分布变化,同样是索引优化的一部分。
三、读执行计划的实用顺序
拿到执行计划后,可以按这个顺序读:先确认驱动表和连接顺序,再看每一步的访问方式,然后看扫描行数与返回行数的差距,最后看排序与临时表。差距越大,越值得先处理过滤条件和索引,而不是先改业务逻辑。
举一个典型场景:按用户编号查最近订单,语句本身不复杂,但如果用户编号字段类型不一致,或者复合索引的顺序放反了,数据库就可能先扫描大量无关订单,再逐行过滤。修掉类型问题、调整索引顺序后,扫描行数会立刻下降,这才是可验证的优化。
四、不要把索引当成万能药
索引能加速查询,也要付出代价。每多一个索引,写入时就要多维护一份结构,更新频繁的表会更明显。索引也不是越多越好:重复索引、几乎不被使用的索引、选择性极低的索引,都会占用空间并拖慢写入。定期清理长期无用的索引,和建索引一样重要。
另外,慢查询有时不在索引上。锁等待、连接池打满、分页偏移量过大,都会表现为查询变慢。分页越往后翻,数据库要跳过的行越多,这时可以考虑用游标分页,让查询从上一次的位置继续,而不是每次都从头数到目标页。
五、可以照着做的排查清单
第一步,复现并记录执行计划,确认是全表扫描、回表过多,还是排序开销大。第二步,核对字段类型与查询参数类型,排除隐式转换。第三步,检查索引列是否被函数、运算或前导列缺失影响。第四步,对照高频查询调整复合索引顺序,把等值条件放在范围条件之前。第五步,改完再跑一次执行计划,对比扫描行数和耗时,确认优化真实生效。第六步,定期复查慢查询日志,把新出现的高频慢查询纳入同一套流程。
慢查询优化没有一招制胜的办法。先用执行计划定位问题,再针对失效原因逐项修复,最后用数据验证结果,这套流程比盲目加索引可靠得多。索引是工具,读懂执行计划才是使用工具的前提。
- 点赞
- 收藏
- 关注作者
评论(0)