key为NULL且possible_keys有值,主因是关联字段字符集或COLLATION不一致,导致优化器无法安全使用索引,须通过SHOW CREATE TABLE比对并统一CHARACTER SET与COLLATION。

EXPLAIN里key=NULL且possible_keys有值,基本就是字符集或COLLATE惹的祸
看到key=NULL但possible_keys不为空,尤其关联字段都是VARCHAR或TEXT类型时,别急着调优SQL,先查字符集。MySQL不是“懒得走索引”,而是根本不敢——不同COLLATION下,相同字符串可能被判定为不等(比如utf8mb4_0900_as_cs区分大小写,utf8mb4_unicode_ci不区分),B+树索引依赖严格有序比较,一旦比较逻辑不可控,优化器只能退化为嵌套循环+逐行转换。
SHOW CREATE TABLE比对COLLATE才是真动作,别信建表语句注释
很多团队只看Java实体类或DDL脚本里的VARCHAR(32)就以为“类型一致”,实际两张表的COLLATION可能一个是utf8mb4_unicode_ci、另一个是utf8mb4_general_ci,甚至同为utf8mb4但排序规则冲突。必须执行:
SHOW CREATE TABLE tb_a; SHOW CREATE TABLE tb_b;
重点比对JOIN字段的COLLATE列;更稳妥的是直接查元数据:
SELECT column_name, character_set_name, collation_name
FROM information_schema.COLUMNS
WHERE table_name IN ('tb_a', 'tb_b') AND column_name = 'join_field';
ALTER TABLE统一COLLATE要同时指定CHARACTER SET和COLLATION
只改COLLATION不改CHARACTER SET可能无效,反之亦然。正确写法是:
ALTER TABLE tb_b MODIFY join_field VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
注意:
- 必须用
MODIFY或CHANGE,ALTER COLUMN在MySQL中不支持COLLATE变更 - 如果字段有默认值或约束,需一并显式带上,否则会被清空
- 线上执行前务必在从库验证,某些版本会锁表
CONVERT()或COLLATE强制转换只是临时止痛,反而掩盖问题
有人写ON a.name = CONVERT(b.name USING utf8mb4)或ON a.name = b.name COLLATE utf8mb4_unicode_ci,看起来能跑通,但EXPLAIN里key依然为NULL,且Extra出现Using where; Using join buffer——说明转换发生在JOIN之后的过滤阶段,CPU开销翻倍,数据量上来就崩。这类写法只适合应急回滚,不能进主干代码。
真正容易被忽略的是:字符集不一致的问题往往藏在“看起来一样”的地方。比如开发环境建表用了默认utf8mb4_unicode_ci,测试环境手动执行过ALTER TABLE ... CONVERT TO ...却漏了COLLATE参数,导致新字段变成utf8mb4_general_ci。这种差异不会报错,只有压测时EXPLAIN才突然暴露。


















