MySQL 导出 CSV 中文乱码:字符集链路从头讲一遍原创
版本说明
本文基于 MySQL 8.0 验证,SELECT ... INTO OUTFILE 和 secure_file_priv 机制自 5.x 起长期稳定,命令通用。
# 1. 为什么导出的 CSV 会乱码
MySQL 导出 CSV 中文乱码,本质是字符集在导出链路的某个环节被错误转换,而不是数据本身坏了。一次导出要经过"数据库存储字符集 → 客户端连接字符集 → 导出文件编码 → 打开文件的软件(Excel 等)默认编码"这条链路,任何一环不匹配都会花屏。定位思路应该是先确认数据在数据库里到底是不是正常的,再逐环检查导出路径,最后才是"文件本身没问题,但打开它的软件猜错了编码"这种情况。
# 2. 导出 CSV 的两种方式
方式一:mysql 命令行 + sed 拼接(适合快速导小表)
mysql -u root -p -e "select * from wordpress.wp_users" | sed 's/\t/","/g;s/^/"/;s/$/"/;s/\n//g' > /tmp/wp_users.csv
mysql -e 默认输出是以 \t(制表符)分隔字段的纯文本,本身并不是标准 CSV 格式,这条 sed 的作用是把制表符替换成 ",",再给每行首尾加上引号,拼成"看起来像"标准 CSV 的格式。这种方式的局限:如果字段内容本身就包含逗号、引号或换行符,简单的字符串替换无法正确转义,会把 CSV 结构搞乱——只适合字段内容干净、临时导个小表的场景。
方式二:SELECT ... INTO OUTFILE(推荐,MySQL 原生支持转义)
SELECT * INTO OUTFILE '/data/db/mysql3306/filedir/a.csv'
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM wordpress.wp_users;
2
3
4
5
OPTIONALLY ENCLOSED BY '"' 表示只有需要转义的字段(含逗号、引号、换行等特殊字符)才会被引号包裹,比 sed 拼接更符合标准 CSV 规范。这个方式受 secure_file_priv 参数限制,导出路径必须落在这个参数指定的目录内:
[mysqld]
secure_file_priv = /data/db/mysql3306/filedir
2
为什么会有这个限制:INTO OUTFILE 是由 mysqld 进程本身在服务端写文件,如果不限制目录,恶意用户理论上可以通过这条语句往服务器任意路径写文件(比如覆盖系统文件),secure_file_priv 就是为了收窄这个攻击面。如果没设置该参数,默认导出目录通常是 /var/lib/mysql-files/(或未设置则整个功能被禁用),可以按需改成 /tmp/ 或其他专用目录,但生产环境不建议设为空(允许任意路径)。
# 3. 解决中文乱码的两个关键点
第一,确认导出时的字符集:如果表本身用的是 utf8mb4,但客户端连接字符集是 latin1 或其他不匹配的编码,导出过程中就会发生一次隐式的字符集转换,直接导致乱码。用命令行导出前确认连接字符集:
mysql -u root -p --default-character-set=utf8mb4 -e "select * from wordpress.wp_users" > /tmp/wp_users.csv
第二,给 CSV 文件加 UTF-8 BOM 头:即使字符集链路本身没问题,Excel(尤其是 Windows 版)在没有 BOM(Byte Order Mark)标记的情况下,打开 UTF-8 编码的 CSV 文件时经常会按本地默认编码(GBK 等)猜测,导致中文显示成乱码——这不是文件坏了,是 Excel 猜错了编码。给文件开头插入 UTF-8 BOM(EF BB BF 三个字节)可以让 Excel 正确识别:
sed -i '1s/^/\xEF\xBB\xBF/' /tmp/wp_users.csv
验证:用 file 命令确认文件已带 BOM:
file /tmp/wp_users.csv
# 输出应包含: UTF-8 Unicode (with BOM) text
2
再用 Excel 或 vim(:set bomb 后看开头)打开确认中文正常显示。
# 4. 坑
- BOM 头是 Excel 这类 Windows 软件的"习惯性猜测"问题,如果 CSV 是给 Python
pandas/csv模块这类程序化读取,加 BOM 反而可能导致读取时首列字段名前多出一个不可见字符(),程序化消费场景通常不需要加 BOM,按目标消费方决定要不要加。 SELECT INTO OUTFILE导出的文件属主是mysqld运行用户(通常是mysql),如果需要用其他用户读取/传输,注意后续权限调整。secure_file_priv一旦在配置文件里设定,只能通过重启 MySQL 实例修改,不支持运行时动态调整(SET GLOBAL不生效),规划导出目录时提前想清楚。
# 5. 可复用要点
- 乱码排查按链路分层:数据库存储字符集 → 连接字符集 → 文件编码 → 打开软件的编码猜测,逐环确认而不是瞎试。
- 生产环境批量导出优先用
SELECT INTO OUTFILE,字段转义更规范;命令行sed拼接只适合字段干净的小表快速导出。 - 加 UTF-8 BOM 是"取悦 Excel"的手段,程序化读取场景要按消费方需求决定是否需要。
- 01
- Nginx 运维知识地图:从配置基础到反向代理实战 原创07-29
- 02
- MySQL 运维知识地图:从入门配置到高可用排障 原创07-29