索引建了但查询没变快,大概率是“假走索引”:EXPLAIN显示用了索引但rows值大,常见于低选择性字段、联合索引顺序错位、索引覆盖不足、隐式转换或函数操作导致索引失效。

索引建了但查询没变快,大概率不是索引没生效,而是它根本没解决你这条查询真正的瓶颈。
EXPLAIN 显示用了索引,但 rows 值很大
这是最典型的“假走索引”现象。优化器确实选了索引,但扫描行数(rows)接近全表,I/O 没减少多少,自然不快。
- 常见于低选择性字段:比如
gender、status这类只有几个取值的列,即使加了索引,查WHERE status = 1仍可能扫一半数据 - 联合索引顺序错位:有
INDEX(a, b, c),但只用WHERE b = ? AND c = ?,跳过a就无法利用前缀,只能当全表扫描用 - 索引覆盖不足:查
SELECT *,而索引只含a, b,MySQL 不得不回表取其余字段,回表次数多时比全表扫描还慢
WHERE 条件触发隐式转换或函数操作
索引列被“包装”后,B+树结构就失效了,优化器只能放弃使用该索引。
-
WHERE DATE(create_time) = '2024-01-01'→ 改成create_time >= '2024-01-01' AND create_time -
WHERE phone = 13800138000(phone是VARCHAR)→ 必须写成WHERE phone = '13800138000',否则发生隐式转换 -
WHERE name LIKE '%张'→ 前导通配符无法用 B+ 树索引,要么改右模糊('张%'),要么换FULLTEXT或 ES
写法或配置让优化器主动绕开索引
MySQL 的成本模型会权衡“走索引 + 回表”和“直接全表扫描”的开销,小表或统计信息不准时,它宁愿全扫。
- 表太小(比如几千行):全表扫描可能只读 1–2 个数据页,比走索引树再回表更省
-
ANALYZE TABLE没更新:优化器依赖过时的行数/分布统计,误判索引成本,执行ANALYZE TABLE your_table可强制刷新 -
OR条件混用索引与非索引列:WHERE indexed_col = 1 OR unindexed_col = 2,优化器常弃索引选全表 - 使用
!=、NOT IN、IS NULL(对可空索引列)等,多数情况下无法高效走索引
索引本身成了负担,反而拖慢整体性能
索引不是免费的。它在写入时同步更新,还会占用内存和磁盘,影响缓冲池效率。
- 高频写入表上建了多个冗余索引:每次
INSERT/UPDATE/DELETE都要维护所有索引,写变慢,innodb_buffer_pool_size被挤占,读也连带变慢 - 索引字段太长或类型不合适:比如对
TEXT列建全文索引却用于等值查询,或对超长VARCHAR(500)建普通索引,B+树深度增加、页分裂频繁 - 未考虑覆盖索引:如果经常查
SELECT a, b FROM t WHERE c = ?,建INDEX(c, a, b)比单列INDEX(c)更有效,避免回表
真正卡住查询的,往往不是“有没有索引”,而是“这个索引是否匹配查询模式、数据分布和访问路径”。别急着加索引,先看 EXPLAIN 的 key 和 rows,再确认条件写法和字段类型——这些细节一错,索引就形同虚设。


















