rows值高表明优化器误判,主因是索引设计不当或统计信息过期;需结合type、key、extra分析,并用force index验证,优先构建匹配查询顺序的复合索引。

为什么 EXPLAIN 里的 rows 值高得离谱?
rows 是 MySQL 估算的「可能扫描的行数」,不是实际返回行数。它高通常意味着优化器没选对索引,或者现有索引根本没法用。常见诱因包括:索引列顺序和查询条件不匹配、用了函数或表达式导致索引失效、WHERE 条件中存在隐式类型转换、或者索引本身选择性太低(比如对性别字段建了单独索引)。
- 真实场景里,
rows = 100000却只返回 5 行,基本说明走了全表扫描或低效索引 -
rows值本身不直接等于性能瓶颈,但它暴露了优化器“误判”——而误判往往源于统计信息不准或索引设计缺陷 - 不要只盯着
rows看,一定配合type(比如ALL或index)、key(实际用的索引名)、Extra(比如Using filesort或Using temporary)一起读
怎么快速验证索引是否真的被用了?
别信 EXPLAIN 的第一眼判断,先强制走索引对比执行时间。用 USE INDEX 或 FORCE INDEX 显式指定,再看 rows 和实际耗时变化:
EXPLAIN SELECT * FROM orders WHERE status = 'shipped' AND created_at > '2024-01-01';
如果 key 是空,但你知道有个 idx_status_created,就试:
EXPLAIN SELECT * FROM orders FORCE INDEX (idx_status_created) WHERE status = 'shipped' AND created_at > '2024-01-01';
- 强制索引后
rows明显下降 + 执行变快 → 原索引设计或统计信息有问题 - 强制后更慢 → 那个索引本身不适合该查询(比如
status区分度太低,前导列无效) -
SHOW INDEX FROM orders查Cardinality值,如果status的基数只有几十,却把它放复合索引最左位,大概率白搭
重建索引前必须检查的三件事
索引不是越多越好,重建前先确认底层问题是不是出在别处:
- 检查
ANALYZE TABLE orders是否刚执行过——旧统计信息会让优化器持续误判,尤其在大批量写入后 - 查
SELECT COUNT(DISTINCT status) / COUNT(*) FROM orders,如果结果 status 选择性极差,不适合作为复合索引首列 - 看查询是否含
OR、NOT IN、LIKE '%abc'这类天然难走索引的写法;这类情况即使加索引,rows也很难降下来
什么样的复合索引能真正压低 rows?
核心是「匹配查询过滤顺序 + 覆盖高频字段」。例如查询常带 WHERE user_id = ? AND created_at > ? ORDER BY updated_at DESC LIMIT 20:
错误做法:建
(user_id, updated_at)——created_at条件被跳过,优化器仍可能弃用正确做法:优先
(user_id, created_at),把ORDER BY字段updated_at加到最后(如果不需要覆盖查询,可省略),或直接建覆盖索引(user_id, created_at, updated_at, status, amount)减少回表等值条件列(
=、IN)放最左,范围条件列(>、BETWEEN)紧随其后,最多一个ORDER BY字段若需避免Using filesort,必须和前面过滤列连续且方向一致(都 ASC 或都 DESC)别为了“看起来完整”把太多字段塞进索引——每多一列,索引体积和维护成本都涨,反而可能让优化器更倾向放弃它
rows 值大本身不是病灶,是症状。真正卡住人的,往往是索引列顺序和查询模式之间那层薄但关键的错位,以及没人去更新的统计信息。改完记得 ANALYZE TABLE,否则优化器下次还是按老印象猜。











