key 为 NULL 但 possible_keys 有值,说明优化器识别到索引却主动放弃,常见于选择性低、统计信息过期、未用最左前缀或成本误判,实际执行多为全表或索引全扫描,性能风险高。
key 为 NULL 但 possible_keys 有值,说明什么
这代表 mysql 优化器「看得到」索引(possible_keys 非空),但主动放弃了它。常见原因包括:索引选择性太低、统计信息过期、查询条件未覆盖索引最左前缀、或优化器误判成本更低。此时 key 显示为 null,实际执行大概率是 type: all(全表扫描)或 type: index(索引全扫描),性能风险明确。
- 检查
WHERE条件是否用上了联合索引的最左字段,比如索引是(a,b,c),但查询只写了WHERE b = ?,那该索引不会被选中 - 运行
ANALYZE TABLE table_name;更新统计信息,尤其在大批量 INSERT/DELETE 后 - 用
FORCE INDEX临时验证:如果加了强制索引后rows显著下降,说明优化器判断有偏差
possible_keys 有多个,key 只选一个,怎么判断它挑得对不对
possible_keys 是候选池,key 是最终拍板的那一个。优化器依据的是「预估扫描行数 rows」和「索引访问成本」,但它不考虑缓存、IO 延迟或并发竞争——这些现实因素常导致它选错。
- 对比各候选索引的
key_len:若key_len远小于索引定义长度(比如联合索引(a,b,c)总长 20 字节,但key_len = 5),说明只用了第一个字段a,后续字段没生效,可能不如另一个更窄的单列索引 - 观察
Extra列:出现Using filesort或Using temporary时,即使key不为NULL,也意味着排序/分组无法走索引,应优先优化这部分 - 用
EXPLAIN FORMAT=JSON查看used_columns和range_analysis,比传统格式更能暴露优化器权衡过程
key 和 possible_keys 都为空,是不是一定没建索引
不一定。更可能是以下情况:
- 查询字段根本不在任何索引里(比如
WHERE status + 1 = 5这种表达式计算,会跳过索引) - 对索引字段用了函数或类型隐式转换,例如
WHERE DATE(create_time) = '2026-06-01'或WHERE user_id = '123'(user_id是INT类型) - 表使用了不支持索引下推的存储引擎(如 MyISAM 对部分
LIKE '%abc'无效),或 MySQL 版本太老( -
SELECT *查询主键表时,优化器可能直接走聚簇索引全扫描,不显示key(但type是index,不是ALL)
key_len 值比预期小,为什么联合索引没全用上
key_len 是优化器实际使用的索引字节数,它严格按「最左前缀匹配」和「字段是否允许 NULL」计算。值偏小,基本等于只用到了联合索引开头的一两个字段。
- 确认
WHERE中是否跳过了中间字段:索引(a, b, c),查询WHERE a = 1 AND c = 3→b缺失,c不会走索引 - 注意数据类型:
VARCHAR(255)字段在 UTF8MB4 下最多占 1020 字节,但key_len只算实际存储长度 + 2 字节长度头;而CHAR是定长,key_len固定 - NULL 字段额外占 1 字节:如果索引字段定义为
name VARCHAR(50) NULL,那它比NOT NULL多 1 字节,key_len会体现出来
key 和 possible_keys 不一致时,得快速判断是索引设计问题、查询写法问题,还是优化器本身的局限——这时候别光盯着 EXPLAIN 输出,直接查 information_schema.STATISTICS 看索引基数,或者用 sys.schema_index_statistics(MySQL 5.7+)看真实命中率。











