MySQL 8.0升级后老表字符集不会自动变更,仍保持原latin1或utf8(utf8mb3)编码;新建库表才默认utf8mb4,若连接层用utf8mb4而字段实际为旧编码,将引发隐式转换、乱码或Incorrect string value错误。

默认字符集改了,但老表一个字节都不会自动变——这是所有问题的起点。
老表的字符集不会随 MySQL 8.0 升级而改变
MySQL 8.0 把 character_set_server 和新建库/表的默认值设为 utf8mb4,但升级过程**完全不触碰已有对象**。你查 SHOW CREATE TABLE t1,如果原来建的是 CHARACTER SET latin1 或 utf8(即 MySQL 的 utf8mb3),那它还是原样躺在那里。
- 连接层默认用
utf8mb4,而字段实际是latin1→ 触发隐式转换,查询可能“看似命中”实则漏数据(比如空格、大小写比较行为突变) - 应用插入 emoji 或生僻汉字时,
utf8字段会报Incorrect string value,因为只支持 BMP 平面 -
SHOW VARIABLES LIKE 'character_set%'显示全是utf8mb4,但SHOW CREATE DATABASE db_name里仍是latin1→ 这种“表面一致、底层割裂”最易误判
ALTER TABLE CONVERT TO utf8mb4 的真实代价
执行 ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci 看似一步到位,但它本质是全表重建:读取每行原始字节、按旧字符集解码、再按新字符集重编码、写入新页。对大表来说,这不只是慢,更是风险点。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 持有
MDL排他锁,DML 阻塞时间 = 数据量 × 解码+重编码耗时 - 若源字符集识别错误(比如把
latin1当作utf8mb4解码),数据直接损坏,不可逆 -
ROW_FORMAT=COMPACT表中含长VARCHAR字段时,索引键长可能超767字节限制 → 报错Specified key was too long,需提前设innodb_large_prefix=ON并改ROW_FORMAT=DYNAMIC
mysqldump 导出时没指定 --default-character-set 就等于埋雷
导出命令没加 --default-character-set=latin1,而源表真是 latin1,mysqldump 就会把原本存的 0xC4E3(中文“我”的 latin1 编码)当成 utf8mb4 字节流去解析,再转成 utf8mb4 存进 dump 文件 → 导入后变成乱码“鎴”之类,且无法还原。
- 导出前必须先确认:
SHOW CREATE TABLE t1看DEFAULT CHARSET,不是看character_set_server - dump 文件开头几行用
head -20 dump.sql | grep CHARSET检查建表语句里的字符集是否仍为原值 - 批量替换时别无脑 sed:
sed -i 's/CHARACTER SET utf8/CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci/g',要确保只改目标字段,不误伤注释或字符串字面量
真正麻烦的从来不是改配置或跑一条 ALTER,而是得在字节层面理清“这串二进制当初是怎么存进去的、现在想让它怎么被解读”——少一次 SHOW CREATE TABLE,就多一分数据错位的风险。

















