JOIN关联字段排序规则不一致必然导致索引失效;需用SHOW FULL COLUMNS或information_schema.COLUMNS比对两端collation是否完全相同;ALTER TABLE修改时须同时指定CHARACTER SET和COLLATION,且需验证EXPLAIN中key是否生效。

JOIN 关联字段字符集或排序规则(COLLATION)不一致,索引必然失效——这不是配置问题,是 MySQL 优化器的硬性限制。只要 EXPLAIN 中 key 为 NULL 且 type 是 ALL 或 index,而关联字段本身明明建了索引,第一反应就该查字符集。
怎么快速确认是不是字符集/排序规则惹的祸
别猜,直接查定义:
- 用
SHOW FULL COLUMNS FROM table_name LIKE 'column_name'看Collation列,重点比对两端字段的值是否完全一致(比如utf8mb4_unicode_ci和utf8mb4_0900_as_cs就算不一致) - 查
information_schema.COLUMNS一次性对比多个表:SELECT column_name, character_set_name, collation_name FROM information_schema.COLUMNS WHERE table_name IN ('t1', 't2') AND column_name = 'join_key'; - 注意:即使
CHARACTER SET都是utf8mb4,只要COLLATION不同,照样失效;MySQL 比较时实际依赖的是排序规则,不是字符集本身
ALTER TABLE 修改字段 COLLATION 的实操要点
改字段定义必须同时指定 CHARACTER SET 和 COLLATION,只改一个等于白干:
- 正确写法:
ALTER TABLE t2 MODIFY join_key VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - 错误写法:
ALTER TABLE t2 CONVERT TO CHARACTER SET utf8mb4;—— 这只改表默认值,不改已有字段的COLLATION - 大表操作会锁表重建索引,务必在低峰期执行;若支持
ALGORITHM=INPLACE(如 MySQL 8.0+ 且字段无全文索引),可加ALGORITHM=INPLACE减少锁表时间 - 改完立刻
SHOW CREATE TABLE验证,再跑一遍EXPLAIN,确认key列出现索引名才算生效
为什么不能靠 CONVERT / COLLATE 在 SQL 里临时绕过
有人试过在 ON 条件里写 ON u.name = CONVERT(o.name USING utf8mb4) 或 ON u.name = o.name COLLATE utf8mb4_unicode_ci,这看似“解决问题”,实际埋雷:
-
CONVERT()和COLLATE都是对右表字段做运行时转换,导致o.name上的索引完全无法使用 - 执行计划可能退化为
Using join buffer,内存压力陡增,大结果集容易 OOM - 这种写法把字符集适配逻辑从 DDL 层推到了 SQL 层,后续任何人写 JOIN 都得重复抄一遍,极易遗漏或写错
- 连接层的
@@collation_connection如果和字段 COLLATION 不匹配(比如 JDBC 连接串只设了charset=utf8mb4没设collation),照样触发隐式转换
真正稳定的解法,是让关联字段的 COLLATION 完全一致——不是“看起来一样”,是 SHOW FULL COLUMNS 输出的字符串一字不差。改表结构麻烦一次,换来长期查询稳定;临时 SQL 补丁省事一时,换来的是持续的性能抖动和排查成本。

















