key_len为null或偏小通常因未触发索引最左前缀,如联合索引(a,b,c)中仅where b=1或where a=1 and c=1时分别无法使用或仅用a列;需检查type、possible_keys与key,避免函数操作、合理设计联合索引顺序,并评估字段区分度与更新频率。

EXPLAIN 看不出 key_len?说明索引没用上
很多同学跑完 EXPLAIN 发现 key_len 是 NULL 或远小于预期,第一反应是“索引建错了”,其实更可能是查询条件没触发索引最左前缀。比如联合索引 (a, b, c),只查 WHERE b = 1 就不会走索引;WHERE a = 1 AND c = 1 也只会用上 a 那一列。
实操建议:
- 检查
EXPLAIN的type字段:如果是ALL或index,基本等于全表/全索引扫描,得优先优化 - 确认
possible_keys有值但key为NULL,说明优化器认为走索引不划算——可能因为结果集太大,或统计信息过期,先ANALYZE TABLE - 避免在索引列上做函数操作:
WHERE YEAR(create_time) = 2024会让create_time索引失效,改用WHERE create_time >= '2024-01-01' AND create_time
联合索引字段顺序怎么排?看查询模式,不是按字段重要性
很多人按“主键 > 时间 > 状态”这种直觉排序建联合索引,结果发现慢查询照旧。关键不是字段本身重不重要,而是 WHERE、ORDER BY、GROUP BY 中的实际组合和顺序。
实操建议:
- 把等值查询(
=、IN)字段放最左,且按高频组合排列。例如常查WHERE tenant_id = ? AND status = ?,就建(tenant_id, status),别反过来 - 范围查询(
>、BETWEEN、LIKE 'abc%')字段只能放在等值字段之后,且它右边的字段无法再用于索引查找(但可用于覆盖索引) - 如果还有
ORDER BY create_time DESC,且该字段也常出现在 WHERE 中,可考虑加到联合索引末尾,避免额外排序
哪些字段不适合建索引?别盲目加
索引不是越多越好。写多读少的表,或者低区分度字段(如 gender、is_deleted),建索引反而拖慢 INSERT/UPDATE,还浪费内存和磁盘。
实操建议:
- 用
SELECT COUNT(DISTINCT col) / COUNT(*)算区分度:低于 0.01 的字段(比如百万行里只有 2 个值),一般不值得单独建索引 - 大文本字段(
TEXT、VARCHAR(2000))不要直接建索引,要用前缀索引:INDEX (content(100)),但得验证前 100 字符是否真能区分大部分值 - 频繁更新的字段,索引维护成本高,尤其在高并发写场景下,宁可让个别慢查走临时表或应用层缓存
慢查询还没下去?检查是不是锁等出了问题
有时候 EXPLAIN 显示走了索引,执行时间却忽高忽低,甚至卡住几秒,大概率不是索引问题,而是锁竞争或事务没及时提交。
实操建议:
- 查
SHOW ENGINE INNODB STATUS\G里的TRANSACTIONS和LOCK WAIT部分,看有没有事务长时间未提交,或被锁住 - 用
SELECT * FROM information_schema.INNODB_TRX ORDER BY trx_started LIMIT 5找运行太久的事务 - 确认应用代码里有没有忘记
COMMIT,或事务里混了 SELECT FOR UPDATE + 大量业务逻辑,导致锁持有时间过长
真正难的不是建对索引,而是把查询、索引、事务、锁这几层影响串起来看。一个 WHERE 条件改写,可能同时影响执行计划、锁粒度、甚至主从延迟。别只盯着 EXPLAIN 输出那几行。











