utf8mb4_0900_ai_ci导致索引失效的主因是排序规则变更引发collation不匹配或隐式类型转换,而非字符集本身慢;当字段collation与连接层或查询中指定的collation不一致时,mysql无法复用索引。

为什么 utf8mb4_0900_ai_ci 会让索引失效
不是字符集本身慢,而是排序规则变更导致隐式类型转换或 collation 不匹配,让 MySQL 放弃走索引。5.7 常用 utf8mb4_general_ci,而 8.0 默认是 utf8mb4_0900_ai_ci,二者对大小写、重音、空格的处理逻辑不同。当字段定义用旧 collation,但连接层或比较表达式用了新规则(比如 WHERE name = 'abc' COLLATE utf8mb4_0900_ai_ci),MySQL 就无法复用原有索引。
- 检查字段实际 collation:
SHOW FULL COLUMNS FROM tbl LIKE 'name',看Collation列是否为utf8mb4_general_ci或其他非默认值 - 检查查询中是否混用 collation:比如应用执行了
SET NAMES utf8mb4,但没指定 collation,服务端会按默认utf8mb4_0900_ai_ci解析,而字段仍是utf8mb4_general_ci→ 不匹配 - EXPLAIN 结果中若出现
Using where; Using index是理想状态;若变成Using where且key为空,大概率是 collation 冲突导致索引被跳过
ALTER TABLE CONVERT TO CHARACTER SET 后为什么还是慢
只改字符集不指定排序规则,MySQL 会自动套用该字符集的“默认 collation”——8.0 下就是 utf8mb4_0900_ai_ci。如果你老表原用 utf8mb4_unicode_ci 或 utf8mb4_bin,直接 CONVERT TO CHARACTER SET utf8mb4 就会强制切换 collation,可能引发排序结果变化、索引重建失败、甚至 WHERE 条件因大小写敏感性改变而命中率下降。
- 正确写法必须显式带
COLLATE:ALTER TABLE tbl CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci - 修改后务必重建索引:
ALTER TABLE tbl DROP INDEX idx_name, ADD INDEX idx_name (name),因为索引是按 collation 构建的,换规则后旧索引结构已不兼容 - 别依赖
mysqldump --default-character-set=utf8mb4自动迁移:它不会保留原 collation,dump 出的 CREATE TABLE 语句里 collation 会被覆盖为默认值
连接层 SET NAMES 导致的隐式转换陷阱
应用连 8.0 时如果仍写 SET NAMES utf8,MySQL 会把它当作 utf8mb3 处理,但服务端内部会静默升级为 utf8mb4;而如果字段是 utf8mb4_general_ci,客户端声明的是 utf8mb3,就会触发字符集隐式转换,索引失效。
- 统一用
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci(或你选定的 collation),避免服务端自行推导 - 在 JDBC 连接串中显式指定:
?characterEncoding=utf8mb4&connectionCollation=utf8mb4_unicode_ci - 检查
SHOW VARIABLES LIKE 'collation%',确认collation_connection和字段 collation 一致,否则WHERE col = ?中的参数会被转换后再比对
JSON 字段 + utf8mb4_0900_ai_ci 的双重坑
JSON_EXTRACT 返回的是 JSON 类型值,但和字符串字面量比较时会隐式转为字符串,并受当前 collation 影响。8.0 默认 utf8mb4_0900_ai_ci 是大小写不敏感、带语言感知的,而 5.7 常用的 utf8mb4_general_ci 虽也不区分大小写,但对某些 Unicode 字符(如德语 ß、土耳其语 İ)处理逻辑不同,导致相同 SQL 在两版中走不同执行路径。
- 不要写
WHERE JSON_EXTRACT(data, '$.name') = 'Alice'—— 这会触发隐式转换,且无法走虚拟列索引 - 应先建 STORED 虚拟列:
ADD COLUMN name_v VARCHAR(100) AS (JSON_UNQUOTE(JSON_EXTRACT(data, '$.name'))) STORED,再建索引 - 比较时加
COLLATE utf8mb4_bin强制二进制语义:WHERE name_v COLLATE utf8mb4_bin = 'Alice',避免 collation 变更影响行为











