filtered 表示存储引擎返回的行中最终被 server 层保留的比例,即“留下的百分比”,非过滤掉的比例;其值基于 rows 预估扫描行数计算(rows × filtered/100),反映 WHERE 条件在 server 层的过滤效率。

filtered 字段不是“过滤掉的百分比”,而是“留下的百分比”
很多人看到 filtered = 18 就以为“过滤掉了 82%”,其实反了:filtered 表示存储引擎返回的行中,**最终被 server 层保留下来、满足 WHERE 条件的比例**。它不是对全表的过滤率,而是对 rows 列所代表的“预估扫描行数”的留存率。
举个实际例子:rows = 174,filtered = 18,意味着 MySQL 预估:索引扫描先拿到 174 行,再在 server 层用剩余条件(比如没走索引的字段)逐行判断,最后只留下约 31 行(174 × 0.18 ≈ 31)。这个过程会带来额外 CPU 和行锁开销。
-
filtered = 100是理想状态:索引已覆盖全部查询条件,server 层无需二次过滤 filtered 不一定差,但要查清原因——是索引缺失?还是隐式类型转换导致索引失效?- 该值来自统计信息估算,不是精确值,所以不能单看数字大小下结论
为什么 filtered 低 ≠ 查询一定慢,但往往暴露索引设计问题
低 filtered 常见于以下真实场景:
- 复合索引顺序错位:比如建了
(a, b),但查询条件是WHERE b = ? AND a = ?,b无法走索引前缀,server 层被迫对所有a匹配行再过滤b - WHERE 中用了函数或表达式:
WHERE YEAR(create_time) = 2025→ 索引失效,rows变成全表或全索引扫描,filtered往往很低 - 字符集/排序规则不一致:如关联字段一边是
utf8mb4_0900_as_cs,另一边是utf8mb4_general_ci,导致隐式转换,索引下推失败 - JSON 字段或生成列未建函数索引:条件写成
data->'$.status' = 'done'却没在该路径上建虚拟列+索引
注意:filtered 低本身不直接导致 I/O 增加,但它常和 type = ALL 或 type = index 同时出现,这时才真正危险。
如何快速定位 filtered 低的根本原因
别只盯着 filtered 数字,要结合 type、key、Extra 三列交叉验证:
- 如果
type是range或ref,但filtered ,优先检查 <code>key是否用了预期索引,以及Extra是否含Using where(表示 server 层过滤) - 如果
Extra出现Using index condition,说明用了 ICP(索引条件下推),此时filtered应该较高;若仍低,大概率是 ICP 没生效(比如条件含 OR、子查询、不支持下推的函数) - 执行
SHOW INDEX FROM table_name对比查询条件字段是否都在key所指索引的最左前缀中 - 用
ANALYZE TABLE table_name更新统计信息,排除因过期统计导致的误判
小技巧:把原 SQL 的 WHERE 条件拆成单字段逐一 EXPLAIN,能快速定位哪个条件拖垮了 filtered。
filtered 高也不等于万事大吉
filtered = 100 看着很美,但可能掩盖更严重的问题:
- 索引太宽:比如建了
(a, b, c, d, e)五列联合索引,只为支持WHERE a = ? AND b = ?,虽然filtered = 100,但索引体积大、更新开销高、缓存效率低 - 覆盖索引误用:
SELECT *+filtered = 100,看似高效,实则可能因回表被优化器自动转为全索引扫描(type = index),I/O 反而比走主键还多 - 统计信息偏差大:某些版本 MySQL 在数据倾斜严重时(如 99% 的
status = 'pending'),filtered会严重高估有效过滤能力,导致优化器选错执行计划
真正关键的,从来不是 filtered 这个孤立数字,而是它背后暴露出的索引与查询条件之间的匹配质量——这东西没法靠调参解决,得一行一行看 EXPLAIN 输出,再一列一列对字段和索引。











