索引失效是因字段、连接层、sql字面量三者collation不匹配导致mysql主动弃用索引;须用show full columns、select @@collation_connection、show create table分别确认三层真实值,统一为相同collate(如utf8mb4_0900_as_cs)并显式配置连接参数。

索引失效不是没建索引,而是 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
常见坑:username 字段是 utf8mb4_unicode_ci,但 @@collation_connection 是 latin1_swedish_ci 或 utf8mb4_0900_as_cs。哪怕字符集都是 utf8mb4,只要 COLLATE 不同,就触发隐式转换。
ALTER TABLE 修改字段 COLLATION 必须同时指定 CHARACTER SET 和 COLLATE
只改 CHARACTER SET 不改 COLLATE,等于白干。MySQL 比较时依赖的是排序规则,不是字符集本身。
- 错误写法:
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4—— 只改表默认值,不改已有字段的COLLATE - 错误写法:
ALTER TABLE users MODIFY username VARCHAR(50) CHARACTER SET utf8mb4—— 不带COLLATE,字段Collation仍保持原样 - 正确写法:
ALTER TABLE users MODIFY username VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs—— 必须同时指定二者,且值要和关联字段完全一致
大表操作会锁表重建索引,务必在低峰期执行;MySQL 8.0+ 且字段无全文索引时,可加 ALGORITHM=INPLACE 减少锁表时间。改完立刻 SHOW CREATE TABLE 验证字段定义,再跑一遍 EXPLAIN,确认 key 列出现索引名才算生效。
连接层 collation_connection 不统一,改表也白搭
字段层改对了,但应用连进来时 @@collation_connection 还是错的,WHERE 条件里的字符串字面量仍会被转成另一套规则再比对,索引照样失效。
- JDBC 连接串必须显式加:
?useUnicode=true&characterEncoding=utf8mb4&collationConnection=utf8mb4_0900_as_cs - PDO 要设
PDO::ATTR_EMULATE_PREPARES => false并在 DSN 中声明:charset=utf8mb4;collation=utf8mb4_0900_as_cs - 命令行客户端启动时加
--collation-server=utf8mb4_0900_as_cs
漏掉 collation= 这部分,前面所有表和字段改得再整齐,查询时仍可能触发转换。
最麻烦的是它不报错,只悄悄变慢;EXPLAIN 里 key 为 NULL、type 是 ALL,就是信号。真正要对齐的从来不是“字符集”,而是每个字段、每次连接、每条 SQL 字面量背后那个具体的 COLLATION 值。











