filtered=100%不代表查询快或索引用得好,仅说明服务器层未额外过滤,可能正进行全表扫描;它是优化器对存储引擎返回行中满足where条件比例的估算,需结合rows和type综合判断。

filtered = 100% 不代表查询快,也不代表索引用得好——它只说明服务器层没做额外过滤,但可能正趴在全表扫描上喘气。
filtered 是什么,为什么不能单看数值
filtered 是优化器估算的「存储引擎返回的行中,最终满足 WHERE 条件的比例」。它不是扫描总行数的占比,而是针对 rows 列所表示的「引擎预估返回行数」做的二次估算。
常见误解是:filtered 越高越好。实际恰恰相反:filtered 越低(比如 5%、10%),说明 WHERE 条件越精准,引擎返回的无效行越少;filtered 接近 100%,往往意味着条件几乎没筛掉数据——要么没索引,要么索引没生效,要么条件本身区分度极低。
-
filtered=100%+type=ALL+rows=852341→ 典型全表扫描,I/O 压力大 -
filtered=100%+type=ref+rows=12→ 索引高效命中,条件精准,可接受 -
filtered=5%+type=ALL+rows=1000000→ 引擎扫了百万行,只留下 5 万,但 I/O 和 CPU 已经浪费了
filtered 低但性能差?重点看是否回表或索引覆盖不全
当 filtered 显著偏低(如 1%~10%),而 key 列显示用了索引,大概率是「索引没覆盖所有 WHERE 字段」,导致引擎先按索引取一批行,再逐行回表判断其余条件。
例如:SELECT * FROM orders WHERE user_id = 123 AND status = 'paid',只在 user_id 上建了单列索引:
-
key显示用了idx_user_id -
rows=200表示引擎从索引查出 200 行 -
filtered=8%表示其中仅约 16 行满足status = 'paid',其余 184 行白读了
解决方式不是调优 filtered 数值,而是补联合索引:ALTER TABLE orders ADD INDEX idx_user_status (user_id, status)。注意字段顺序:等值查询字段放前,user_id 区分度高,必须在左。
filtered 高但 key 为空?说明索引根本没被选中
filtered=100% 却 key=NULL,这是危险信号——优化器压根没考虑任何索引,直接走全表扫描(type=ALL)。此时 filtered 的 100% 是“虚假繁荣”,因为没索引,引擎返回全部行,服务器层自然无需再过滤。
典型诱因包括:
- WHERE 中对索引字段用了函数:
WHERE YEAR(create_time) = 2023→ 改成create_time BETWEEN '2023-01-01' AND '2023-12-31' - 隐式类型转换:
WHERE mobile = 13800138000(mobile是VARCHAR)→ 改成WHERE mobile = '13800138000' - 统计信息过期:
ANALYZE TABLE orders强制刷新,再看EXPLAIN FORMAT=JSON中的condition_filtering_pct是否变化
filtered 和 rows/type 必须一起读,缺一不可
单独看 filtered 没意义。它的价值永远依附于 rows 和 type:
-
type决定访问方式(ALL、ref、range…),是效率基线 -
rows是引擎预估要触碰的数据量,直接影响 I/O 成本 -
filtered是在这个rows基础上,还能筛掉多少——但它不改变rows本身
真正健康的组合是:type 尽量接近 const/eq_ref,rows 在百以内,filtered 在 80%~100% 之间(说明条件基本靠索引完成,没额外开销)。如果 rows 过大,哪怕 filtered=100%,也得优化索引或拆分查询。
最容易被忽略的是:filtered 的估算依赖表级统计信息,而这些信息不会实时更新。大表批量写入后不 ANALYZE TABLE,filtered 可能严重失真,误导你误判索引效果。











