mysqldump如何以正确编码与排序规则导出数据库 解决乱码问题
修改--default-character-set后文件大小变化但乱码表现完全一致,说明参数没有真正影响到最终落盘的字节流,问题集中在三个高频场景:
- shell重定向输出时被系统/终端默认编码强制转码
- MySQL全局配置覆写了连接层字符集,传入的参数未生效
- 历史遗留的编码元数据不匹配(假latin1存utf8内容),转码逻辑错位
分步排查修复
第一步:绕过shell重定向排除管道转码问题
mysqldump通过>重定向输出时,内容会经过stdout通道,部分系统终端默认编码非utf8,会在这个环节对内容做强制转码,无论mysqldump本身输出什么编码,最终写盘的文件都会被损坏。
直接用mysqldump内置的-r(--result-file)参数绕过stdout,直接写入目标文件:
mysqldump --default-character-set=utf8mb4 --no-tablespaces -u database_user -p -r /home/username/database_name-direct-write-20220712.sql database_name
导出后执行file /home/username/database_name-direct-write-20220712.sql查看文件编码,如果显示为UTF-8编码,打开文件校验德、西语特殊字符正常,后续导出统一使用-r参数写文件即可。
第二步:校验连接字符集是否被配置覆写
如果-r参数导出的文件依然乱码,说明问题出在MySQL连接层字符集被强制覆写。登录源数据库执行以下命令,查看连接时实际生效的字符集配置,不要仅参考库表的默认编码:
SHOW VARIABLES LIKE 'character%'; SHOW VARIABLES LIKE 'collation%';
重点核对character_set_client、character_set_connection、character_set_results三个值,必须和你传入的--default-character-set参数一致。如果不一致,按以下方式排查:
- 检查源端MySQL配置文件(Linux默认路径为
/etc/my.cnf、/etc/mysql/my.cnf,Windows为my.ini),删除[client]、[mysqldump]段下硬编码的default-character-set配置,或统一修改为utf8mb4 - 执行
SHOW VARIABLES LIKE 'init_connect';,如果返回值包含SET NAMES相关语句,临时注释该配置后重启会话重试导出——该配置会在所有连接建立后自动执行字符集设置,直接覆盖你传入的参数
第三步:修复历史遗留假latin1编码问题
以上两步都无效时,对应你提到的「库级默认编码为latin1、业务表用utf8_general_ci排序规则」的情况,属于典型的历史遗留编码不匹配问题:表元数据标记为utf8,但历史写入数据时连接层使用latin1编码,实际存储的是utf8字节,元数据和真实存储编码不匹配。平时查询时客户端自动做了隐式转码所以看起来数据正常,导出时转码逻辑错位就会出现乱码,这也是你修改三次--default-character-set参数乱码表现一致的核心原因。
这种场景不要依赖自动转码,要让mysqldump直接读取原始存储字节导出:
mysqldump --default-character-set=latin1 --no-tablespaces -u database_user -p -r /home/username/database_name-raw-20220712.sql database_name
导出后不要直接导入,先用sed批量替换dump文件里的字符集声明,把latin1标记改成实际存储对应的utf8mb4,不要用GUI文本编辑器做替换,避免编辑器自动转码造成二次损坏:
sed -i 's/CHARSET=latin1/CHARSET=utf8mb4/g' /home/username/database_name-raw-20220712.sql
替换完成后再导入目标实例即可。
导入注意事项
- 导入时给mysql客户端同样指定
--default-character-set=utf8mb4参数,避免导入环节再次出现转码错误 - 导入完成后随机抽查包含特殊字符的字段,确认内容正常后再切换业务流量
- 跨版本从MySQL5.7导入MySQL8.0时,提前在目标实例关闭
foreign_key_checks和unique_checks可以提升导入速度,导入完成后再恢复即可
内容的提问来源于stack exchange,提问作者versvs

