explain中key为null且type为all是collation不一致的铁证,mysql优化器因b+树依赖确定排序逻辑而主动弃用索引;必须通过show full columns或information_schema.columns比对字段collation_name是否完全相同,并用alter table modify同时指定character set和collate修正,且需同步@@collation_connection。

EXPLAIN 显示 key 为 NULL 就是 COLLATION 不一致的铁证
不是“可能不走索引”,而是 MySQL 优化器看到 collation_name 不同,直接拒绝使用索引——因为 B+ 树依赖确定的排序逻辑,两边对“张三”和“张三”的比较结果可能不同(比如一个区分大小写、一个不区分),数据库宁可全表扫描也不敢错。典型信号:EXPLAIN 中被驱动表的 key 列为 NULL、type 是 ALL 或 index,哪怕只查几万行也秒变分钟级。
别只比 character_set_name,collation_name 必须一字不差
即使两字段都是 utf8mb4,只要 collation_name 不同(比如 utf8mb4_unicode_ci ≠ utf8mb4_0900_as_cs),JOIN 就失效。MySQL 比较时实际用的是 collation,不是 charset。
- 查字段真实定义:用
SHOW FULL COLUMNS FROM t1 LIKE 'user_id'和SHOW FULL COLUMNS FROM t2 LIKE 'user_id',逐字比对Collation列 - 更省事:查
information_schema.COLUMNS一次性对比:SELECT table_name, column_name, character_set_name, collation_name FROM information_schema.COLUMNS WHERE table_name IN ('t1','t2') AND column_name = 'user_id'; - 别漏连接层:
SELECT @@collation_connection—— 字符串字面量(如'张三')按这个规则解释,它和字段 collation 不匹配,照样触发隐式转换
ALTER TABLE MODIFY 才是正解,ON 子句里加 CONVERT 或 COLLATE 没用
ON t1.name = CONVERT(t2.name USING utf8mb4) 或 ON t1.name = t2.name COLLATE utf8mb4_unicode_ci 这类写法语法能过,但索引照旧失效。因为这是运行时转换,优化器无法下推,t2.name 上的索引根本不会被命中。
- 真正要改的是字段定义本身,且必须同时指定
CHARACTER SET和COLLATE:ALTER TABLE t2 MODIFY user_id VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs;
- 错误写法:
ALTER TABLE t2 CONVERT TO CHARACTER SET utf8mb4(只改表默认值,不碰字段 collation);或MODIFY ... COLLATE utf8mb4_0900_as_cs(漏了CHARACTER SET,MySQL 可能覆盖成意外值) - 改完立刻执行
SHOW CREATE TABLE t2确认字段定义里同时出现CHARACTER SET utf8mb4和COLLATE utf8mb4_0900_as_cs,别信SHOW FULL COLUMNS的缓存显示
大表改字段会锁表重建索引,别跳过验证步骤
MySQL 8.0+ 若满足条件可加 ALGORITHM=INPLACE 减少影响,但必须提前测试是否适用。改完不是就完事了:
- 先跑
EXPLAIN看原 SQL 的key列是否出现索引名、type是否变成ref或eq_ref - 如果仍显示
Using join buffer,检查 JOIN 条件里是否还有其他字段没对齐(比如net_way也参与了ON,但它没改 collation) - 必要时手动
ANALYZE TABLE更新统计信息,否则优化器可能因陈旧数据继续弃用索引
最常被忽略的点是:字段改对了,连接层 @@collation_connection 没同步,或者应用连接串里漏了 collation= 参数。这时候你改得再整齐,SQL 里的 'abc' 还是按老规则解析,关联前就失真了。











