Filtered 表示MySQL优化器预估的满足WHERE条件的行数占比,计算公式为rows × (Filtered / 100),用于估算实际参与后续操作的有效行数,其准确性依赖统计信息更新与条件复杂度。

Filtered 字段到底反映什么
Filtered 是 EXPLAIN 输出中一个常被误读的列,它表示MySQL 优化器预估的、在完成表访问(如全表扫描或索引扫描)后,满足 WHERE 条件的行数占该次访问总行数的百分比。注意:它不是“过滤掉的行”,而是“留下来的有效行占比”。
这个值是基于统计信息(ANALYZE TABLE 更新)和条件选择性估算出来的,不涉及实际执行,也不代表最终结果集大小(那由 rows × Filtered 粗略体现)。
- 它只出现在
EXPLAIN FORMAT=TREE或 MySQL 5.7+ 的传统EXPLAIN中(列为filtered) - 值域为 0–100,单位是百分比(显示为浮点数,如
10.00表示 10%) - 若没有 WHERE 条件或优化器无法估算,可能显示
100.00
用 Filtered 预估扫描有效载荷的实操逻辑
预估“有效载荷”——即实际参与后续计算(JOIN、排序、聚合等)的行数——关键在于把 rows 和 Filtered 连起来算:
rows × (Filtered / 100) ≈ 优化器认为真正“有用”的行数
比如某步骤输出:
id: 1 table: orders type: ALL rows: 124800 filtered: 0.10
说明:全表扫描约读 12.5 万行,但其中只有约 124800 × 0.001 = 125 行满足 WHERE 条件。这 125 行才是 JOIN 或 ORDER BY 的输入基数。
- 如果
filtered持续低于 1,大概率存在隐式类型转换、函数包裹字段(如WHERE YEAR(created_at) = 2023)、或缺少对应索引导致无法下推过滤 - 若
rows很大但filtered极低(如 0.01),即使加了索引,也可能因索引区分度差(如对status ENUM('A','B','C')建单列索引)而无效
Filtered 不准的常见原因与验证方法
Filtered 是估算值,不准很常见,尤其当统计信息陈旧或条件复杂时:
-
ANALYZE TABLE未执行或采样不足(默认采样 10 页,可通过innodb_stats_persistent_sample_pages调整) - 使用了用户变量、子查询、或
JSON_CONTAINS()等优化器难以建模的表达式 - 多列联合条件中,优化器只对最左前缀做统计,其余列过滤效果被低估(例如
WHERE a=1 AND b IN (…),若只有(a,b)索引,b的选择性可能未被充分计入)
验证是否靠谱,最直接的方式是对比:
- 执行
EXPLAIN记下rows和filtered - 实际执行带
SQL_NO_CACHE的查询,用SHOW STATUS LIKE 'Handler_read%'查看真实读取行数(如Handler_read_next+Handler_read_rnd_next) - 若真实过滤率远高于
filtered(比如估算 5%,实测 60%),说明优化器严重低估了条件效力,可能应强制走某索引(USE INDEX)或重写 WHERE
Filtered 对执行计划决策的实际影响
Filtered 本身不直接决定是否用索引,但它影响优化器对“使用某索引后还需回表多少行”的成本判断:
- 对于
ref或range访问,优化器会结合索引匹配行数(rows)和剩余条件的filtered估算最终输出行 - 如果
filtered被低估,优化器可能放弃本该更优的索引,转而选全表扫描(因为误判索引路径总代价更高) - 在多表 JOIN 中,某表的低
filtered值会拉高其驱动表优先级——优化器倾向先处理“过滤后剩得最少”的表,所以一个filtered=0.5的小表,可能比filtered=100.0的大表更早被选为驱动表
真正容易被忽略的是:Filtered 的数值稳定性依赖于 innodb_stats_auto_recalc 是否开启,以及表变更量是否达到 10% 阈值。线上大表如果长期未 ANALYZE,又经历大量 INSERT/DELETE,Filtered 就只是个参考数字,不能直接代入性能公式。


















