乱码根源在备份时未指定--default-character-set,导致mysqldump用character_set_client(如utf8mb4)错误解析latin1表,产生双重编码;还原时再以latin1解码已损坏的UTF-8字节流,必然乱码。

乱码不是还原命令执行失败,而是备份那一刻就错了——mysqldump没指定--default-character-set,导致它用当前 session 的 character_set_client(常是 utf8mb4)去读一张实际是 latin1 的表,把字节当 UTF-8 解一遍再写进 SQL 文件,造成“双重编码”。还原时再用 latin1 去解析这个已错的 UTF-8 字节流,中文必然变 æäº›æå 或问号。
查清表的真实字符集,别信 SHOW VARIABLES
很多人一上来就改 my.cnf 或跑 SET NAMES,但根本问题出在表本身。MySQL 不会自动按库级默认值覆盖字段级定义。
-
SHOW CREATE TABLE table_name—— 看DEFAULT CHARSET=xxx和每个VARCHAR字段后有没有CHARACTER SET xxx - 同一库不同表可能混用
utf8mb4、latin1甚至gbk,必须逐个查,不能假设统一 -
SHOW VARIABLES LIKE 'character_set%'里的client或connection只影响当前会话,和mysqldump行为无关
还原命令必须带 --default-character-set,且值要和表真实编码一致
不加这个参数,mysql 客户端默认用编译时设定的字符集(通常是 latin1)解码整个 SQL 文件,哪怕你的库是 utf8mb4,汉字也会被截断或错映射。
- 正确写法:
mysql --default-character-set=utf8mb4 -u root -p db_name < backup.sql - 错误写法:
mysql -u root -p db_name < backup.sql(缺参数) - 如果报
Unknown character set: 'utf8mb4',说明客户端太老,升级mysql命令行工具,或临时用utf8(但 emoji 会丢) - Windows 下别用
cmd执行——它的代码页常是GBK,会二次污染;改用PowerShell或Git Bash
SQL 文件开头没 SET NAMES,手动补上并确保位置正确
--default-character-set 控制客户端如何解码 SQL 文本,但不控制服务器如何解码 INSERT 语句里的字符串字面量。如果文件里没有初始化连接编码的语句,字段级字符集可能被忽略。
- 打开
backup.sql,在**第一行**插入:SET NAMES utf8mb4; - 这行必须在任何
CREATE DATABASE、USE或CREATE TABLE之前 - 如果文件里已有
SET NAMES latin1;,必须替换成utf8mb4,否则它会覆盖命令行参数 -
SET NAMES≠--default-character-set:前者发指令给服务器,后者控制客户端本地解码行为,两者作用阶段不同,不能互相替代
还原后 SELECT 还是乱码?重点检查三处存储层
命令跑成功不代表数据对了。MySQL 有四层编码:客户端、连接、数据库、表/列。其中任意一层不匹配,SELECT 出来就是乱的。
- 运行:
SELECT @@character_set_client, @@character_set_connection, @@character_set_results;—— 三个值必须都是utf8mb4 - 查表结构:
SHOW CREATE TABLE t1;—— 确认ENGINE=InnoDB DEFAULT CHARSET=utf8mb4,且字段定义里显式写了CHARACTER SET utf8mb4 - 查数据是否真存坏了:
SELECT HEX(col) FROM t1 LIMIT 1;—— 如果中文显示为E68891(UTF-8 编码),说明存得对;如果是C4E3(GBK 编码),说明原始数据就错了,得靠反向转码修复
最容易被忽略的是:同一个 SQL 文件里可能混着不同字符集的表,而 ALTER TABLE CONVERT TO CHARACTER SET 只改元数据和后续写入行为,不会重解已存的乱码字节。真乱了的数据,得靠“反向 encode/decode”拉回来,不是改配置能救的。


















