确认COLLATION不一致导致JOIN索引失效需用SHOW FULL COLUMNS比对关联字段Collation,若EXPLAIN中possible_keys有值但key为NULL且type为ALL,则证实问题;须ALTER TABLE同时指定CHARACTER SET和COLLATE统一,连接层collation_connection也必须对齐。

直接改字段的 COLLATION,别在 SQL 里临时加 COLLATE 或 CONVERT——后者会让索引彻底失效,且掩盖真实问题。
怎么确认是 COLLATION 不一致导致 JOIN 索引失效
别靠经验猜,用两条命令直接比对:
-
SHOW FULL COLUMNS FROM table1 LIKE 'join_col'和SHOW FULL COLUMNS FROM table2 LIKE 'join_col',重点看两行输出里的Collation列是否完全相同(比如utf8mb4_0900_as_cs和utf8mb4_unicode_ci就算不一致) - 跑一次
EXPLAIN SELECT * FROM table1 JOIN table2 ON table1.join_col = table2.join_col,如果possible_keys有值但key为NULL,且type是ALL或index,基本就是 COLLATION 惹的祸
ALTER TABLE 修改字段 COLLATION 的实操要点
只改 CHARACTER SET 不改 COLLATE 等于白干——MySQL 比较时依赖的是排序规则,不是字符集本身。
- 正确写法:
ALTER TABLE table2 MODIFY join_col VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs(必须同时指定二者,且值要和另一张表的字段完全一致) - 错误写法:
ALTER TABLE table2 CONVERT TO CHARACTER SET utf8mb4——这只改表默认值,不改已有字段的COLLATE - 大表操作会锁表重建索引,务必在低峰期执行;MySQL 8.0+ 且字段无全文索引时,可加
ALGORITHM=INPLACE减少锁表时间 - 改完立刻执行
SHOW CREATE TABLE table2验证字段定义,再跑一遍EXPLAIN,确认key列出现索引名才算生效
为什么不能在 ON 条件里用 COLLATE 临时修复
这类写法看似“能跑”,实则把问题从 DDL 层推到了 SQL 层,埋下长期隐患:
-
ON u.name = o.name COLLATE utf8mb4_0900_as_cs会让o.name上的索引完全无法使用 - 执行计划可能退化为
Using join buffer,内存压力陡增,大结果集容易 OOM - 后续任何人写 JOIN 都得重复抄一遍,极易遗漏或写错;ORM 自动生成的 SQL 更不可能自动补上
- 这种写法掩盖了结构缺陷,让 DBA 和开发者都误以为“问题已解决”,下次出问题更难排查
连接层和 JOIN 字段的 COLLATION 必须同时对齐
即使两张表字段的 COLLATE 已统一,如果应用连接进来时 @@collation_connection 不匹配,WHERE 条件里的字符串字面量仍会被转成另一套规则再比对,索引照样失效。
- Java JDBC 连接串加:
?useUnicode=true&characterEncoding=utf8mb4&collationConnection=utf8mb4_0900_as_cs - PHP mysqli 连接后立刻执行:
mysqli_set_charset($conn, 'utf8mb4'),并确认SET NAMES utf8mb4 COLLATE utf8mb4_0900_as_cs - 验证是否生效:
SELECT COLLATION('test'),返回值应与字段的Collation完全一致
最易被忽略的一点:即使两个字段 CHARACTER SET 都是 utf8mb4,只要 COLLATION 不同(比如一个用 utf8mb4_general_ci,另一个用 utf8mb4_0900_as_cs),索引照样失效——这不是配置疏漏,是 MySQL 优化器的硬性限制。


















