索引失效源于字段、连接、sql字面量三层collation不一致,mysql主动弃用索引;需分别用show full columns、select @@collation_connection等查三层真实值,并统一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?
MySQL 实际比对的是排序规则(COLLATE),不是字符集本身。只改 CHARACTER SET 不改 COLLATE,基本等于白忙。
- 错误写法:
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 不同,MySQL 就拒绝用索引做等值连接,改走 Block Nested-Loop。
- 查两边字段真实 COLLATION:
SELECT character_set_name, collation_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name IN ('t1', 't2') AND column_name = 'name'; - 统一方式:要么改字段
MODIFY,要么在ON条件里临时强制:ON t1.name = t2.name COLLATE utf8mb4_0900_as_cs(仅临时缓解,不推荐长期依赖)
最麻烦的是它不报错,只悄悄变慢;EXPLAIN 里 type 变成 ALL 或 index、rows 暴增,才是关键信号。











