根本原因是字段字符集或排序规则(collation)与查询条件不一致,导致mysql隐式转换使索引失效;需同步统一连接层、字段层和sql层的collation配置。

为什么 WHERE 条件用了索引字段却走全表扫描?
根本原因常是字段字符集或排序规则(COLLATION)和查询条件不一致,MySQL 无法直接比较,被迫隐式转换——一旦对索引列做转换,索引就失效。
典型现象:EXPLAIN 显示 type: ALL 或 key: NULL,哪怕字段明明建了索引;用 SHOW CREATE TABLE 查看字段定义,发现 CHARSET 是 utf8mb4,但 COLLATION 是 utf8mb4_unicode_ci,而连接里默认用的是 utf8mb4_general_ci 或更糟的 latin1_swedish_ci。
- 连接层(client/connection)的
collation_connection必须和字段的COLLATION兼容,否则 WHERE 中字符串字面量会被转成另一套规则再比对 - 不同
COLLATION可能导致排序权重不同,MySQL 认为“不能安全使用索引范围扫描”,干脆放弃 - 即使字段和连接都是
utf8mb4,若一个是utf8mb4_0900_as_cs(大小写敏感),另一个是utf8mb4_unicode_ci(不区分大小写),也照样失效
如何查清当前连接和字段的排序规则是否匹配?
别猜,直接查。两步到位:
- 查字段真实
COLLATION:SHOW FULL COLUMNS FROM table_name LIKE 'column_name';,看Collation列 - 查当前连接生效的规则:
SELECT @@collation_connection, @@character_set_client, @@character_set_results; - 特别注意:如果应用用 JDBC、PHP PDO 等,它们可能在连接串里显式指定
charset=utf8mb4,但没指定collation,MySQL 就会 fallback 到该 charset 的 default collation(如utf8mb4_0900_ai_ci),而你的字段可能是老版本建的utf8mb4_unicode_ci
示例:字段是 name VARCHAR(64) COLLATE utf8mb4_unicode_ci,但连接返回 @@collation_connection = utf8mb4_0900_as_cs → 不匹配,索引失效。
修复方案:从连接、字段、到 SQL 写法的三层控制
不是改一个地方就行,得同步收敛:
- 连接层:在 MySQL 客户端连接时强制指定,比如命令行加
--default-character-set=utf8mb4 --collation-server=utf8mb4_unicode_ci;应用连接串中明确写?charset=utf8mb4&collation=utf8mb4_unicode_ci(JDBC)或charset=utf8mb4, collation=utf8mb4_unicode_ci(PDO) - 字段层:批量修正(谨慎!):
ALTER TABLE table_name MODIFY column_name VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;,注意这会锁表,且需确认存量数据无乱码风险 - SQL 层临时兜底:在 WHERE 中显式声明 collation,比如
WHERE name = 'abc' COLLATE utf8mb4_unicode_ci,但这只是补救,不可长期依赖,性能仍略损
容易被忽略的坑:JOIN 和 ORDER BY 同样受影响
不只是 WHERE。JOIN 条件两边字段 collation 不一致,会导致无法使用索引驱动表;ORDER BY 字段若 collation 和连接不一致,即使有索引也会退化为 filesort。
-
EXPLAIN FORMAT=JSON看used_columns和using_filesort字段,比传统EXPLAIN更准 - 触发隐式转换的不只是字符串字面量,还有
CONCAT()、UPPER()等函数结果,只要类型/规则不匹配,索引就悬空 - 复制环境尤其危险:主库字段用
utf8mb4_unicode_ci,从库因版本差异默认用utf8mb4_0900_ai_ci,可能导致从库执行计划异常、延迟飙升
真正麻烦的不是改配置,而是确认所有链路——客户端、中间件、ORM、备份恢复脚本——都对齐了同一个 COLLATION。漏掉任意一环,索引失效就随时回来。










