执行计划进阶:读懂filtered和rows的组合,精准判断索引设计质量

📅 2026/7/13 15:14:24 👁️ 阅读次数 📝 编程学习
执行计划进阶:读懂filtered和rows的组合,精准判断索引设计质量

大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

用EXPLAIN看执行计划,大多数人只看三样东西:type是不是ALL、key是不是NULL、Extra有没有Using filesort。但如果只看到这个程度,你离“真正读懂执行计划”还有一段距离。

真正该看的,是rows和filtered的组合

单独看rows,你只知道“要扫多少行”,但不知道索引筛得干不干净。单独看filtered,你只知道“过滤比例”,但不知道绝对数量有多大。只有把两者放在一起看,才能判断索引设计到底行不行。今天把rows和filtered的组合逻辑彻底拆开讲一遍。

一、rows和filtered分别是什么?

rows:优化器估算的、需要扫描的行数。它是一个相对值,不是精确值,但量级决定了查询成本的基数。

filtered:存储引擎返回的行中,满足剩余WHERE条件的比例。比如rows=1000、filtered=10%,表示存储引擎返回了1000行,经过WHERE条件过滤后最终只留下100行。filtered=100%表示索引精准定位,无需额外过滤;filtered越低,说明索引筛掉的“垃圾”越少,回表后还要过滤掉大量数据。

关键认知:rows是“扫描量”,filtered是“过滤效率”。两者相乘(rows × filtered%)就是你最终需要返回的行数,但更重要的是——filtered越低,意味着索引帮上忙的就越少。

二、三种典型组合及其诊断结论

组合一:rows小、filtered高(✅ 索引设计优秀)

rows=100,filtered=100%。优化器估算扫描100行,全部命中,不需要额外过滤。

诊断结论:索引精准定位了数据,回表代价极小。说明索引列选择性和顺序都很好。

组合二:rows小、filtered低(⚠️ 索引用了,但没完全用)

rows=100,filtered=10%。优化器估算扫描100行,但最终只留下10行,90%的行在回表后被过滤掉。

诊断结论:索引只帮你定位到了一小部分数据,但WHERE条件中还有重要字段不在索引里,导致回表后大量过滤。说明索引设计“缺了东西”——可以把被过滤的字段加进索引,或调整索引列顺序。

组合三:rows大、filtered高(⚠️ 索引命中精准,但扫描范围太大)

rows=100万,filtered=100%。优化器估算扫描100万行,全部命中。

诊断结论:索引确实帮你精准定位了,但问题在于——你要查的数据本身就有一百万行。这说明索引选择正确,但查询条件本身过滤性太差(比如查“2026年全年订单”),或者数据量本身就大。这时候优化方向不是改索引,而是考虑分区表、物化视图或业务层面缩小查询范围。

组合四:rows大、filtered低(❌ 索引设计有问题,最差组合)

rows=100万,filtered=5%。扫描100万行,最终只留5万行。

诊断结论:索引几乎没起什么作用——扫描了大量数据,回表后又过滤掉95%。这是最需要优化的组合。大概率是索引列选择性极差(比如status只有两三个值),或者索引顺序完全不对。优先考虑重建索引或重新设计查询条件。

三、真实案例:从rows和filtered组合定位问题

一个订单表有500万行数据,status字段只有三个值(PAID/UNPAID/REFUND),索引为(status)。业务SQL:

sql

SELECT * FROM orders WHERE status = 'PAID' AND create_time > '2026-01-01';

EXPLAIN输出:

typekeyrowsfilteredExtra
refidx_status150万30%Using where

诊断:rows=150万(status='PAID'占了约30%的数据),filtered=30%(还要再过滤create_time)。组合来看,索引只筛掉了70%的数据,回表后还要再过滤70%,实际有效数据只有45万行。扫描150万行、回表150万次,只为了拿45万行数据——浪费巨大。

优化:改成复合索引(status, create_time)。新EXPLAIN:

typekeyrowsfilteredExtra
refidx_status_ctime45万100%Using index condition

效果:rows从150万降到45万,filtered从30%升到100%,查询时间从4.2秒降到0.3秒。

四、在多表JOIN中,filtered决定了驱动顺序

很多人不知道,filtered在JOIN中还有一个关键作用——它直接影响优化器选择哪个表作为驱动表

优化器在选择JOIN顺序时,会估算“驱动表行数 × filtered”作为被驱动表的匹配次数。比如:

  • 表A:rows=10000,filtered=50% → 估算匹配5000次

  • 表B:rows=5000,filtered=90% → 估算匹配4500次

优化器可能会选择表B作为驱动表,即使它的rows更小——因为filtered更高意味着更少的无效匹配。这就是为什么有时候你加了个索引,JOIN顺序突然变了——filtered变了,优化器的估算变了。

诊断建议:如果你发现JOIN顺序不合理,检查被驱动表的连接字段是否有索引,以及filtered是否偏低。

五、总结

EXPLAIN的rows和filtered,单独看都没什么用,组合起来才是诊断索引设计质量的关键工具:

rowsfiltered诊断方向
✅ 优秀保持
⚠️ 索引缺字段加入被过滤的列
⚠️ 数据量本身大分区/物化视图/缩小范围
❌ 索引设计问题重建索引或改写SQL

下次用EXPLAIN的时候,别只看type和key。把rows和filtered两个数字摆在一起,问问自己:这个组合告诉我什么?回答这个问题,你就能从“会看EXPLAIN”升级到“会用EXPLAIN做诊断”。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~