索引失效的根本原因是字段COLLATION、连接层@@collation_connection及SQL字面量三者任意一对不匹配,导致MySQL无法安全使用索引;必须用SHOW FULL COLUMNS、SELECT @@collation_connection和SHOW CREATE TABLE精准比对,统一为utf8mb4_0900_as_cs并显式配置连接参数。

查清哪一层的字符集或排序规则不一致
索引失效不是“字段有没有索引”,而是“MySQL能不能安全地用它”。真正触发隐式转换的,是字段 COLLATION、连接层的 @@collation_connection、以及 SQL 中字面量(比如 '张三')三者之间任意一对不匹配。最常踩的坑是:字段明明是 utf8mb4,但 COLLATION 是 utf8mb4_unicode_ci,而连接默认用了 utf8mb4_0900_as_cs —— 这俩排序规则在大小写、重音、空格处理上不兼容,MySQL 直接放弃走索引。
必须用 SQL 查,不能猜:
-
SHOW FULL COLUMNS FROM table_name LIKE 'column_name';看Collation列 -
SELECT @@collation_connection, @@character_set_client;看当前连接实际生效的规则 -
SHOW CREATE TABLE table_name;确认表级默认COLLATE是否和字段一致
ALTER TABLE 修改字段 COLLATION 时必须连带指定 CHARACTER SET
只改 CHARACTER SET 不改 COLLATE,或者反过来,基本等于白干。MySQL 比较时看的是排序规则(COLLATE),不是字符集本身。比如你执行:
ALTER TABLE t MODIFY name VARCHAR(50) CHARACTER SET utf8mb4;
—— 这不会改变已有字段的 COLLATION,很可能还是 utf8mb4_general_ci,而目标表字段是 utf8mb4_0900_as_cs,照样不匹配。
正确写法必须显式带上 COLLATE:
ALTER TABLE t MODIFY name VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs;- 批量改多个字段?分步操作,避免锁表太久:
ALTER TABLE t MODIFY a VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs, MODIFY b VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs; - 大表务必低峰期操作;若字段有索引,
MODIFY会重建索引,耗时明显
连接层不统一,改完字段也白搭
字段层改对了,但应用连进来时 @@collation_connection 还是错的,WHERE 条件里的字符串字面量仍会被转成另一套规则再比对,索引照旧失效。JDBC、PDO、命令行客户端都得显式声明,不能依赖服务端默认值。
常见配置方式:
- JDBC 连接串加:
?useUnicode=true&characterEncoding=utf8mb4&collationConnection=utf8mb4_0900_as_cs - PHP PDO 构造时传:
['charset' => 'utf8mb4', 'collation' => 'utf8mb4_0900_as_cs'] - 命令行连接:
mysql --default-character-set=utf8mb4 --collation-server=utf8mb4_0900_as_cs -u user -p - 连接后立即执行:
SET NAMES utf8mb4 COLLATE utf8mb4_0900_as_cs;
验证是否生效:SELECT CHARSET('测试'), COLLATION('测试'); 返回值应与字段 Collation 完全一致。
为什么推荐 utf8mb4_0900_as_cs 而不是 utf8mb4_unicode_ci
MySQL 8.0+ 官方已将 utf8mb4_unicode_ci 标记为 deprecated,且它不区分大小写、不区分重音,和现代业务中常见的精确匹配需求(如用户名、邮箱、身份证号)天然冲突。而 utf8mb4_0900_as_cs 是大小写敏感(cs)、重音敏感(as)、基于 Unicode 9.0 的稳定排序规则,能准确反映业务语义,也更容易让 JOIN 和 WHERE 条件直接命中索引。
但注意:一旦选了 _as_cs,所有关联字段、连接参数、SQL 字面量就必须严格一致 —— 少一个 COLLATE 声明,就可能掉回全表扫描。这不是配置宽松度的问题,是 MySQL 查询优化器的硬性要求。


















