索引失效是因字段、连接层、SQL字面量三者COLLATION不一致导致MySQL主动弃用索引;须用SHOW FULL COLUMNS、SELECT @@collation_connection、SHOW CREATE TABLE精准比对,并统一为utf8mb4_0900_as_cs,修改时必须同时指定CHARACTER SET和COLLATE。

索引失效不是索引没建好,而是 MySQL 在比较前发现字段、连接、SQL 字面量三者 COLLATION 不一致,主动弃用索引——它宁可全表扫描,也不愿返回错误结果。
查清哪一层的 COLLATION 实际不匹配
别猜,直接查三层真实值:
-
SHOW FULL COLUMNS FROM users LIKE 'username'—— 看Collation列,这是字段级真实排序规则 -
SELECT @@collation_connection, @@character_set_client—— 连接层决定'abc'这类字面量按什么规则解释 -
SHOW CREATE TABLE users—— 确认表默认CHARACTER SET和列级是否混用COLLATE
常见坑:字段是 utf8mb4_unicode_ci,但 @@collation_connection 是 latin1_swedish_ci 或 utf8mb4_0900_as_cs,哪怕字符集都是 utf8mb4,只要 COLLATE 不同,就触发隐式转换。
ALTER TABLE 修改字段必须同时指定 CHARACTER SET 和 COLLATE
只改 CHARACTER SET 不改 COLLATE,大概率白忙。MySQL 比对的是排序规则,不是字符集本身。
- 错误写法:
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4—— 会保留原COLLATE,可能仍是utf8mb4_unicode_ci - 正确写法:
ALTER TABLE users MODIFY username VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs - 大表操作会锁表(除非满足
ALGORITHM=INPLACE),务必低峰期执行
改完立刻 SHOW CREATE TABLE 验证字段定义,再跑 EXPLAIN 看 key 是否出现索引名。
连接层不统一,改表也没用
应用连上数据库后,@@collation_connection 决定了 SQL 中字符串字面量的默认排序规则。ORM 或驱动没显式指定时,MySQL fallback 到服务端默认值,极易错配。
- 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 CHARSET('测试'), COLLATION('测试')—— 返回值应与字段Collation完全一致
漏掉 collation= 这部分,前面所有表和字段改得再整齐,查询时仍可能触发转换。
JOIN 场景下两边字段 COLLATION 必须完全一致
两张表关联字段即使都是 VARCHAR、字符集都是 utf8mb4,只要 COLLATION 不同(比如 utf8mb4_unicode_ci vs utf8mb4_0900_as_cs),MySQL 就无法用索引做等值连接,改走 Block Nested-Loop。
- 查法:
SELECT column_name, character_set_name, collation_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name IN ('t1', 't2') AND column_name = 'join_col' - 修复优先级:先统一连接层,再同步修改字段 ——
ALTER TABLE t2 MODIFY join_col VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs - 外键字段同样适用:即使不显式
JOIN,外键约束也会触发隐式字符集比对,可能导致ERROR 1005
最麻烦的不是报错,而是它不声不响变慢;EXPLAIN 里 key 为 NULL、rows 暴增,才是真实信号。


















