WHERE条件未走索引主因是字段基数低,如status仅3个值,选择率超20%时优化器认为全表扫描更优;复合索引应按等值过滤→范围查询→排序顺序排列;前缀索引需验证选择率>95%;key_len偏小表明索引断裂。

为什么 WHERE 条件里用了字段却没走索引?
常见现象是:明明给 status 字段加了索引,SELECT * FROM orders WHERE status = 'paid' 却还是全表扫描。根本原因不是索引没建,而是该字段基数太低——比如只有 'pending'、'paid'、'cancelled' 三种值,重复率极高。
MySQL 的优化器会估算:如果一个值占全表 20% 以上行数,走索引反而比全表扫描更慢(要回表、随机 IO 多)。所以它直接放弃索引。
- 低基数字段(如
gender、is_deleted、status)单独建索引通常无效 - 高基数字段(如
user_id、order_no、email)天然适合做索引主干 - 用
SELECT COUNT(DISTINCT col) / COUNT(*) FROM table粗略算下选择率,低于 5% 才值得单独索引
ORDER BY 和 WHERE 混用时,字段顺序怎么排?
复合索引的字段顺序不是按 SQL 里写的顺序来,而是按“过滤 → 排序 → 查询”优先级排列。比如 SELECT name FROM users WHERE city = ? AND age > ? ORDER BY created_at DESC,索引应该建为 (city, age, created_at),而不是 (city, created_at, age)。
-
WHERE中的等值条件(=、IN)放最前,它们能快速定位数据块 - 范围查询(
>、<、BETWEEN)只能放等值之后,且只能有一个(后续字段索引失效) -
ORDER BY字段必须紧接在可利用的WHERE字段之后,否则无法避免Using filesort - 如果
ORDER BY是多字段且含方向(如ORDER BY a ASC, b DESC),MySQL 8.0+ 才支持混合方向索引;之前版本统一按 ASC 建,DESC字段实际不走索引排序
什么时候该用前缀索引?VARCHAR(255) 不要直接全字段索引
对长文本字段(如 title、url)建完整索引,不仅浪费空间,还会拖慢写入和缓冲池效率。但直接不索引又没法加速模糊查或等值查。
前缀索引是折中解法:只取前 N 个字符建索引。关键是选对 N —— 要让前缀足够“区分”,又别太长。
- 先跑
SELECT COUNT(DISTINCT LEFT(url, 10)) / COUNT(*) FROM pages,看 10 位前缀的选择率是否 > 95% - 逐步增加长度(15、20…),直到选择率不再明显提升,就定这个长度
- 注意:前缀索引不支持
ORDER BY或GROUP BY该字段,也不支持LIKE '%xxx'(左模糊) - 如果业务真需要
LIKE '%xxx',别硬扛,换全文索引或外部搜索引擎
EXPLAIN 里看到 key_len 比预期小,说明什么?
key_len 显示 MySQL 实际用了索引的多少字节。比如你建了 (a, b, c) 索引,但 EXPLAIN SELECT ... WHERE a = ? AND c = ? 发现 key_len 只有 a 的长度,b 和 c 没用上——这说明索引断裂了。
- 原因通常是跳过了中间字段:没有
b的条件,c就无法利用索引排序或查找 -
key_len值偏小,往往对应type: ref或range,但实际扫描行数远超预期,得结合rows和Extra(如Using index condition)一起看 - 对于
UTF8MB4字段,key_len计算要按最大字节数算(比如VARCHAR(100)最多占 400 字节),别拿字符数去比
基数判断和索引字段顺序不是靠经验猜的,是得对着 EXPLAIN 和真实数据分布反复验证的事。尤其当表大了以后,低基数字段混进复合索引,或者前缀长度拍脑袋定,很容易变成隐藏性能杀手。










