rows 与 filtered 是什么
EXPLAIN 的 rows 是优化器估算的要扫描行数,filtered 是这一步条件过滤后预估剩余的行百分比。两者相乘约等于参与后续连接或排序的行数,是执行计划成本估算的主体。读计划时最有价值的判断是「扫描量是否与返回量严重不匹配」:rows 很大而 filtered 很低,说明大量行被读出来又丢掉,通常意味着缺索引或条件写法不当。
基本信息
| 适用版本 | MySQL 5.7 / 8.0(5.7 起 filtered 才有较完整支持) |
|---|---|
| rows 含义 | 优化器估算的扫描行数,来自统计信息 |
| filtered 含义 | 条件过滤后剩余比例,取值 0-100 |
| 估算联动 | rows × filtered% ≈ 进入下一步的行数 |
| 失真原因 | 统计信息陈旧、直方图缺失、条件含函数或隐式转换 |
| 改善手段 | ANALYZE TABLE、补直方图、改写法让条件下推 |
典型使用场景
- 判断慢查询主因:是扫得多还是连接放大
- 评估新索引价值:加索引前先看 rows 与 filtered 的差距
- 排查统计失真:数据大改后计划突变,先刷新统计再判断
- 多表连接分析:定位连接顺序中被驱动表被放大的那一环
- 上线前评审:对比计划中的扫描量与业务预期数据量
对比例子
对比例子
-- 期望形态:rows 小、filtered 高
EXPLAIN SELECT * FROM appdb.orders WHERE user_id = 999 AND status = 1;
-- 反例形态:rows=1500000、filtered=10.00
-- 即估算扫 150 万行才留下 15 万行,说明条件未有效下推或索引选择性差
-- 数据分布变化后刷新统计,再看计划是否恢复合理
ANALYZE TABLE appdb.orders;
EXPLAIN FORMAT=JSON SELECT * FROM appdb.orders WHERE user_id = 999 AND status = 1\G连接场景读法
连接场景读法
-- 被驱动表 filtered 很低,说明每次连接都读出大量行再过滤
EXPLAIN SELECT o.id, u.name
FROM appdb.orders o
JOIN appdb.users u ON u.id = o.user_id
WHERE o.status = 1;
-- 重点看 u 那一行的 rows 与 filtered:rows 越大、filtered 越低,放大越严重
-- 缓解方向:给连接列建索引,或把过滤条件下推到派生表减少被驱动行数字段读法对照
| 字段 | 含义 | 异常表现 |
|---|---|---|
| rows | 估算扫描行数 | 与实际表规模严重不符说明统计失真 |
| filtered | 条件过滤后剩余百分比 | 很低说明条件几乎没减少扫描量 |
| rows × filtered | 进入后续处理的行数 | 远大于最终返回行数即为浪费 |
| Extra: Using where | 条件在服务层过滤 | 配合低 filtered 往往意味着没用上索引 |
| Extra: Using filesort | 需要额外排序 | rows 大时排序开销成倍放大 |
估算值随统计信息变化:大批量写入、删除或迁移后执行计划可能突变,先执行 ANALYZE TABLE 再判断是否真的需要改索引;8.0 还可以为分布不均的列建立直方图来改善估算。
rows=1 不代表一定很快:等值查找命中一行但该行包含大字段(TEXT、BLOB)或返回结果集极大时,网络与磁盘开销依然可观;判断性能要结合返回数据量与 Extra 信息。