索引存在不等于被使用,需用EXPLAIN验证;复合索引需按最左前缀匹配;低基数或函数操作会导致索引失效。

为什么 SHOW INDEX FROM table_name 看起来有索引,但查询还是慢?
因为索引存在 ≠ 索引被用上。MySQL 优化器会根据统计信息、条件写法、数据分布等决定是否走索引。SHOW INDEX 只告诉你“建了什么”,不反映“用了没有”。常见假象是:索引列在 WHERE 子句里出现了,但实际执行时走了全表扫描。
实操建议:
- 先用
EXPLAIN SELECT ...确认key和rows字段 —— 如果key是NULL,说明没走索引;rows接近表总行数,大概率失效了 -
SHOW INDEX中的Seq_in_index值很重要:复合索引(a,b,c),只有查询带a才可能命中;只查b或c,基本失效 - 注意
Cardinality(基数)值过低(比如远小于表行数),说明该列区分度差,优化器可能主动放弃使用
哪些 WHERE 条件会让索引直接失效?
不是所有带索引列的条件都能触发索引查找,MySQL 对表达式敏感,稍一变形就退化为全表扫描。
常见错误现象:
- 对索引列做函数操作:
WHERE YEAR(create_time) = 2023→ 改成WHERE create_time >= '2023-01-01' AND create_time - 隐式类型转换:
WHERE user_id = '123'(user_id是INT)→ 字符串强制转数字,可能丢索引;应确保类型一致 - 使用
!=或NOT IN(尤其右值含NULL)→ 通常无法范围扫描,改用IN或拆分逻辑 - 前导通配符:
WHERE name LIKE '%abc'→LIKE必须左对齐才走索引,'abc%'可以,'%abc'不行
ORDER BY + LIMIT 组合为什么常踩坑?
这个组合看似简单,但极易因索引覆盖不全导致文件排序(Using filesort),甚至绕过索引直接扫全表。
使用场景:分页查最新 20 条订单,按 created_at DESC 排序。
关键判断点:
- 如果
ORDER BY字段不在索引最左前列,或顺序与索引定义相反(如索引是(status, created_at),却ORDER BY created_at DESC),大概率失效 - 复合索引要同时覆盖
WHERE和ORDER BY:例如WHERE status = 1 ORDER BY created_at DESC,理想索引是(status, created_at),且created_at方向需匹配(8.0+ 支持混合方向,但老版本必须一致) -
LIMIT很小(如LIMIT 10)不代表快 —— 如果 MySQL 先排序再截断,仍要处理全部匹配行
重建索引前,先看 information_schema.STATISTICS 和 ANALYZE TABLE
索引结构没问题,但统计信息过期,也会让优化器误判。尤其是大表批量导入后未更新统计信息,很容易选错执行计划。
实操建议:
- 查统计信息是否陈旧:
SELECT TABLE_NAME, CARDINALITY, LAST_UPDATE FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = 'db_name' AND TABLE_NAME = 'table_name'; - 手动更新统计:
ANALYZE TABLE table_name;(轻量级,不锁表);比OPTIMIZE TABLE更安全,后者会重建表 - 避免盲目删重建索引 ——
DROP INDEX+ADD INDEX会锁表(尤其 MyISAM),InnoDB 8.0+ 支持ALGORITHM=INPLACE,但也要看具体操作类型 - 新增索引前,用
pt-duplicate-key-checker或sys.schema_unused_indexes(5.7+)确认是否真有冗余索引
真正难的不是建索引,是理解数据访问模式和优化器怎么“读”你的 SQL。一个 EXPLAIN 后面跟着几行 rows 和 Extra,比十行 CREATE INDEX 更值得盯五分钟。


















