MySQL 8.0升级后默认字符集为utf8mb4,但旧表不自动变更,需逐级检查并修改库、表、列的字符集与排序规则,同时确保连接层三变量统一为utf8mb4,否则引发乱码、索引失效或非法排序规则混合错误。

MySQL 8.0 升级后默认字符集变成 utf8mb4,但旧表不会自动改——这是所有问题的起点。你看到的“变默认”只是对新库/新表生效,已有数据完全不动,混用时立刻出错。
为什么 SHOW VARIABLES 显示 utf8mb4,但查表还是 latin1?
因为 character_set_server 只控制新建对象的默认值,不修改存量。执行 SHOW CREATE TABLE your_table 就会发现 DEFAULT CHARSET=latin1 或 utf8(即 utf8mb3)依然存在。
- 别只看
SHOW VARIABLES LIKE 'character_set%',必须逐个检查库、表、列的实际定义 -
ALTER DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci只改库级默认,不影响已有表 - 真正要动的是表和字段:
ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,但这个命令有前提条件(见下一条)
ALTER TABLE CONVERT TO utf8mb4 失败或静默退化为 COPY 算法
失败常见报错:ERROR 1071 (42000): Specified key was too long;静默退化则表现为锁表时间远超预期,SHOW PROCESSLIST 中卡在 copy to tmp table。
- 根本原因:InnoDB 默认
ROW_FORMAT=COMPACT,且innodb_large_prefix=OFF时,索引键长上限仍是 767 字节。而VARCHAR(255)在utf8mb4下理论最大占 1020 字节(255×4),直接超限 - 必须提前确认并设置:
ALTER TABLE t ROW_FORMAT=DYNAMIC;+ 配置文件中开启innodb_large_prefix=ON(MySQL 5.7+ 已默认 ON,但升级后可能未继承) - 若不满足条件,MySQL 会自动 fallback 到
COPY算法:全表重建、持 SX 元数据锁、阻塞所有 DML —— 生产环境慎用
连接层 charset 不匹配导致索引失效和乱码
现象是:表字段明明是 utf8mb4,EXPLAIN 却显示 type: ALL,SELECT HEX(col) 看到合法 UTF-8 字节,但应用里显示问号或方块。
- 核心链路断裂点在三处变量:
character_set_client、character_set_connection、character_set_results—— 必须全部为utf8mb4 - JDBC URL 必须显式加参数:
&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai;PHP PDO 要设PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4" -
init_connect='SET NAMES utf8mb4'对 super 用户无效,所以 root 或监控账号连上来仍可能是latin1,务必单独验证
排序规则混用引发 Illegal mix of collations 错误
典型报错:Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT)。这不是字符集问题,是 Collation 不兼容。
- MySQL 5.7 旧表常用
utf8mb4_general_ci,8.0 新表默认用utf8mb4_0900_ai_ci,两者无法隐式转换 - JOIN / WHERE / IN 子查询中只要两边字段 Collation 不同,就直接报错,不尝试降级或转换
- 临时解法:
COLLATE utf8mb4_0900_ai_ci强制统一,如WHERE a.col = b.col COLLATE utf8mb4_0900_ai_ci;长期方案是批量修正字段 Collation
真正难处理的不是 utf8mb4 本身,而是它把历史遗留的字符集碎片暴露了出来:表之间、字段之间、连接参数之间、备份导出之间——任何一层没对齐,都会在某个低概率路径上突然崩掉。升级后第一件事不是改配置,而是跑一遍 SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME FROM information_schema.COLUMNS WHERE CHARACTER_SET_NAME IN ('latin1', 'utf8');,把混用点全挖出来再说。


















