干了这么多年 MySQL,最常被同事抓来问的问题之一就是:能不能用一条 SQL 把表里的数据弄成文件?尤其数据分析那边的同事,张口就要 CSV,最好还是带表头、逗号分隔、字符串带引号那种。你要是还在一段段 SELECT 出来再复制粘贴,或者写脚本连上数据库循环打印,那说明你还没用上 SELECT ... INTO OUTFILE。
INTO OUTFILE 是 MySQL 提供的一个服务端数据导出能力,直接写在 SELECT 语句尾部,就能把查询结果写到服务器本地的文件里。它能干什么?一句话:把任意查询结果快速落盘,生成自定义格式的文本文件,方便后续做数据交换、报表交付、ETL 落地、批量导入其他系统。适合谁?后端开发、DBA、数据分析师,凡是需要从 MySQL 里往外拿数据的人,都应该把这套参数摸透。
这讲我专门把 INTO OUTFILE 的各个参数掰开揉碎讲清楚,结合实际场景把坑也一并列出来,保证你看完能直接上手。
1. 一条 SQL 搞定数据导出:INTO OUTFILE 到底做了什么
1.1 从一次线上数据交付说起
之前有个业务方找过来,说第三方合作商需要一批订单明细,要求每天凌晨生成一个 CSV 放到指定目录,格式是:第一行表头,后面每行一条订单,字段用逗号分隔,字符串字段用双引号包起来,金额保留两位小数,时间格式统一成YYYY-MM-DD HH:MM:SS。
如果不用 INTO OUTFILE,常规做法是写个 Python 脚本,连上 MySQL,cursor.execute("SELECT ..."),然后在内存里拼接字符串,再写文件。这样做能跑,但有两层额外开销:数据从 MySQL 服务端传到客户端,再从客户端写到磁盘;中间还要处理字符集转换、特殊字符转义、文件编码等问题。数据量小无所谓,到了百万级、千万级,脚本不仅要等很久,内存和 CPU 也可能成为瓶颈。
INTO OUTFILE 的做法完全不同。同样一条 SELECT,末尾加上INTO OUTFILE '/data/export/order.csv',MySQL 服务端直接把结果集序列化写入文件。文件生成在数据库服务器本地,不走网络回传,速度极快。百万行数据导出,通常几秒到十几秒就完成,和查出来再传回客户端完全是两个量级。
我当时给业务方配置的语句大概是这样的:
SELECT order_id, user_name, amount, create_time INTO OUTFILE '/data/export/order_20240101.csv' CHARACTER SET utf8mb4 FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\n' FROM orders WHERE create_time >= '2024-01-01' AND create_time < '2024-01-02';注意看,这里导出的是纯数据,没有表头。表头我在后续自动化的脚本里手动拼接,或者用 UNION 的方式单独补一行。这点后面细说。
1.2 适用场景与使用边界
INTO OUTFILE 不是万能的,它有非常明确的适用边界。知道它适合什么、不适合什么,比会写语法更重要。
适合的场景主要有四类:
第一,大批量数据导出。几百万行以上的查询结果,需要快速落盘时,INTO OUTFILE 是 MySQL 原生方案里性能最好的一个。服务端直写文件,省掉了中间传输环节。
第二,定期数据交付。比如每天给合作方同步数据、给数仓供数、生成报表数据源。这种场景需求稳定、格式固定,SQL 写好后基本不用改。
第三,跨系统数据迁移。需要把 MySQL 里的数据导入 Hadoop、ClickHouse、Elasticsearch 等系统时,先用 INTO OUTFILE 导出成文本文件,再用对端系统的导入工具批量加载,是常见的数据迁移链路。很多大数据平台的导入工具本身也推荐这种方式。
第四,快速备份单表数据。如果只想备份某几张表的数据,而不是整个库,mysqldump 有点重,INTO OUTFILE 导出的数据文件配合 LOAD DATA INFILE 可以快速恢复。
不适合的场景也很明显:
- 需要导出整个数据库、包含表结构时,应该用 mysqldump,而不是 INTO OUTFILE。INTO OUTFILE 只导数据,不导建表语句。
- 需要导出到客户端本地电脑时,不方便。INTO OUTFILE 只能写到 MySQL 服务器本地路径,如果你连的是云数据库 RDS,文件会落在云服务器上,需要先下载才能到本地。
- 文件已存在会报错,不会覆盖。这一点经常把人搞懵。
- 服务端有
secure_file_priv参数限制,不是想写哪里就能写哪里。
了解这些边界之后,才能在实际项目里把它用对地方。
2. INTO OUTFILE 参数逐个拆解,每个参数解决了什么问题
2.1 FIELDS TERMINATED BY:分隔符选错,后面全是坑
FIELDS TERMINATED BY是导出格式里最基础的参数,指定字段之间的分隔符。默认值是制表符\t,不写的话,出来的文件每个字段用一个 Tab 隔开。
实际工作中,大多数需求是 CSV 格式,所以最常用的是逗号:
SELECT id, name, age INTO OUTFILE '/tmp/user.csv' FIELDS TERMINATED BY ',' FROM users;但这里有个很容易踩的坑:如果字段值本身包含逗号,导出后的文件列数就对不上了。比如用户备注字段remark里存了 "你好,世界" 或 "Tom, Jerry",用逗号做分隔符,数据导入方按逗号拆分,这一行就会多出一列。
解决思路有两个。一个是换一个字段中几乎不可能出现的字符做分隔符,比如|、;、\t、\x01(不可见控制符)。另一个是用ENCLOSED BY给字段加引号包裹,这样即使字段里有逗号,解析方也能识别出这是引号内的逗号。多数时候两个方法配合使用。
分隔符我建议统一在初始化 SQL 的时候就想好,最好形成规范。比如给数仓供数用竖线|,给第三方系统交付用标准 CSV(逗号 + 双引号包裹),给人看的报表用 Tab。规范一旦定了,后续所有导出语句都按同一标准写,省得每个需求都重新讨论一遍格式。
2.2 ENCLOSED BY 与 OPTIONALLY:什么时候需要给字段“穿外套”
FIELDS ENCLOSED BY指定包裹字段值的字符,最常用的是双引号。加上之后,每个字段都会被引号包住:
SELECT id, name, remark INTO OUTFILE '/tmp/user.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '"' FROM users;导出结果可能是这样:
"1","张三","你好,世界" "2","李四","Tom, Jerry"这样外部系统解析时,即使remark里有逗号,只要解析器支持引号包裹,也能正确处理。标准 CSV 格式就是这种玩法。
OPTIONALLY关键字可以加在ENCLOSED BY前面,表示只对字符串类型的字段加引号,数值类型的字段不加。比如:
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'导出的效果就是数值字段裸着,字符串字段带引号:
1,"张三","备注内容"这种格式在很多数据处理工具里更受欢迎,因为数值字段不需要额外的类型转换,导入时少一步处理。
这里要注意,OPTIONALLY ENCLOSED BY只对字符串类型生效,日期时间类型在 MySQL 里也会加上引号。如果你有特殊需求不希望某些字段被引号包裹,可以在 SELECT 里用CAST(字段 AS UNSIGNED)或者CONCAT等方式先转换类型,再决定是否被包裹。
实际项目里,我给第三方系统交付 CSV 时,默认就是TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"',这个组合兼容性最好,绝大部分接收方都认识。
2.3 ESCAPED BY:转义符是把双刃剑
FIELDS ESCAPED BY指定转义特殊字符的符号,默认值是反斜杠\\。
这个参数的作用是:当字段值里包含分隔符、引号、换行符等特殊字符时,MySQL 会在这些字符前面加上转义符,避免解析时产生歧义。
举个例子,字段值里有双引号,导出时会被转义成\";有换行符,会被转义成\n;有制表符,会被转义成\t。
SELECT id, remark INTO OUTFILE '/tmp/user.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '"' FROM users;如果remark的值是他说:"你好",导出结果是:
"1","他说:\"你好\""反斜杠在这里有两个作用:转义了双引号,避免双引号被误认为是字段包裹符。
但也正因为这个特性,导出的文件不完全等同“所见即所得”。如果你的下游系统不支持反斜杠转义,比如一些简单的 CSV 解析脚本,看到\"反而会懵。这时候有两个选择:
一是把ESCAPED BY改成空字符串,即ESCAPED BY '',表示不做任何转义。但这样做的后果是,字段里的分隔符、引号、换行符都会原样输出,非常容易导致数据错位。除非你对数据内容有绝对的把握,否则不建议轻易关闭转义。
二是保持默认转义符,在接收方处理时统一去掉转义符前面的反斜杠。多数成熟的 CSV 解析库(Python 的 csv 模块、Java 的 OpenCSV 等)天生就支持这类转义,直接用就好。
还有一个很容易被忽略的点:当ESCAPED BY设置为空时,NULL 值的输出也会发生变化。默认情况下 NULL 值导出为\N,但关闭转义后,NULL 会变成字符串NULL。内存里看起来没差多少,但在数据导入时,NULL字符串和真正的 NULL 值通常会被区别对待,处理不当就是脏数据。
2.4 LINES 相关参数与 CHARACTER SET:行格式和编码
LINES TERMINATED BY指定行结束符,默认是\n(LF)。
如果导出文件要给 Windows 平台的老旧软件用,可能需要\r\n(CRLF),这时候写成:
LINES TERMINATED BY '\r\n'实际工作中,我很少主动改这个参数,除非对方明确要求。现在主流系统基本都能认\n,改来改去反而容易出问题。
LINES STARTING BY指定每行的起始字符,用得比较少。它会在每行数据前面加上指定前缀。常见用途是给每行加注释符,比如导出 SQL 文件时每行加--,或者给日志类文件每行加固定标记。说实话我用了这么多年,这个参数用到的机会很少,了解即可。
CHARACTER SET参数也很关键。它指定导出文件的字符集,不指定时默认使用数据库连接字符集。如果你导出的是中文数据,建议显式指定CHARACTER SET utf8mb4,避免在其他环节被转成乱码:
SELECT id, name INTO OUTFILE '/tmp/user.csv' CHARACTER SET utf8mb4 FIELDS TERMINATED BY ',' FROM users;特别提醒:如果数据里有 emoji 等四种字节字符,一定不要用utf8,必须用utf8mb4,否则导出会报错或者把字符截断成问号。
2.5 一组参数组合对照表
把常用参数组合整理成一张表,方便对照选择:
| 参数写法 | 输出效果 | 适用场景 |
|---|---|---|
| 默认(不写任何参数) | Tab 分隔,无引号,\N表示 NULL | 快速临时导出,自己人看 |
FIELDS TERMINATED BY ',' | 逗号分隔,无引号 | 简单 CSV,字段内容不含逗号 |
FIELDS TERMINATED BY ',' ENCLOSED BY '"' | 标准 CSV,所有字段带双引号 | 第三方系统对接,字段内容复杂 |
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' | 标准 CSV,仅字符串带引号 | 配合数据分析、ETL 使用,兼容性好 |
FIELDS TERMINATED BY '\t' ENCLOSED BY '"' | Tab 分隔 + 引号包裹 | Excel 直接打开的场景 |
| `FIELDS TERMINATED BY ' | '` | 竖线分隔 |
FIELDS TERMINATED BY '\x01' | 不可见控制符分隔 | 字段内容包含常见可见符号的极端场景 |
这张表我建议存一下,算是这几年踩坑踩出来的一个参考快查。具体选哪种,核心看接收方解析能力,以及字段数据本身的特征。
3. 实操:从零导出一份能被外部系统直接消费的 CSV
3.1 开始之前:确认权限与 secure_file_priv
INTO OUTFILE 不是一个默认可随便用的功能,动手前有三个前置条件需要确认。
第一,账号要有 FILE 权限。可以用这个 SQL 查看:
SHOW GRANTS FOR 'your_user'@'%';如果没有 FILE 权限,会报ERROR 1045 (28000): Access denied。授权操作通常是:
GRANT FILE ON *.* TO 'your_user'@'%'; FLUSH PRIVILEGES;注意FILE权限是全局权限,不是库级或表级权限,授权时要确认是否存在安全风险,生产环境通常只给有导出需求的账号。
第二,确认secure_file_priv参数。这个参数决定了 INTO OUTFILE 能写哪些目录。查看方式:
SHOW VARIABLES LIKE 'secure_file_priv';三种情况:
- 值为
NULL:禁用 INTO OUTFILE,任何目录都写不了。 - 值为空字符串:不限制目录,任意可写路径都能导出。
- 值为具体路径(比如
/var/lib/mysql-files/):只能导出到该目录下。
如果secure_file_priv限制了目录,而你确实需要写其他目录,只能改配置文件重启 MySQL。注意这个参数是只读系统变量,不支持SET GLOBAL动态修改,必须在 MySQL 配置文件my.cnf或my.ini里设置,然后重启实例。生产环境改参数要谨慎,建议先和团队确认窗口期。
第三,确认目标目录的写权限。INTO OUTFILE 文件是由 MySQL 服务进程创建的,文件属主是运行 MySQL 的系统用户(通常是 mysql 用户),所以目标目录必须允许 MySQL 系统用户写入。这个和操作系统权限有关,很多人容易忽略。
3.2 最常见的导出语句长什么样
以一张用户表为例,导出一个给数据分析组用的 CSV:
SELECT id, nickname, phone, email, created_at INTO OUTFILE '/var/lib/mysql-files/user_export.csv' CHARACTER SET utf8mb4 FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\n' FROM users WHERE created_at >= '2024-01-01';这条语句执行后,在/var/lib/mysql-files/目录下生成user_export.csv,每行一条用户记录,字符串字段带双引号,数值字段裸着,utf8mb4 编码。
执行完后可以验证一下文件是否存在、行数是否正确:
wc -l /var/lib/mysql-files/user_export.csv再和数据库里的数量对比:
SELECT COUNT(*) FROM users WHERE created_at >= '2024-01-01';行数一致,基本可以放心交付。
3.3 处理中文、NULL 值和字段内逗号
实际数据里永远藏着各种意外。最常遇到的三类问题:中文乱码、NULL 值、字段内容里的特殊字符。
中文乱码。导出文件字符集和接收方的解析字符集不一致,就会出现乱码。可靠的做法是导出时显式指定CHARACTER SET utf8mb4,同时告诉接收方文件是 UTF-8 编码。如果接收方是 Windows 下的 Excel 等老软件,可能只认 GBK,那导出时就要用:
CHARACTER SET gbk但这种依赖数据库本身支持 GBK 字符集,而且 utf8mb4 里的 emoji 转成 GBK 会报错,所以更推荐导出 utf8mb4 文件,由接收方做转码。
NULL 值。默认情况下 NULL 导出为\N。很多业务系统不认识\N,期望空字符串。可以在 SQL 里用IFNULL显式处理:
SELECT id, IFNULL(nickname, '') AS nickname, IFNULL(phone, '') AS phone INTO OUTFILE '/var/lib/mysql-files/user_export.csv' CHARACTER SET utf8mb4 FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' FROM users;这样导出文件里就不会出现\N了。这个习惯我建议从一开始就养成,因为\N在 CSV 里很容易被下游当成普通字符串处理,而不是空值。
字段内逗号和引号。用户昵称、备注、地址这类字段,内容里很容易出现逗号、引号、换行。用OPTIONALLY ENCLOSED BY '"'之后,字符串字段整体带引号,解析方只要支持标准 CSV 解析,就能正确处理字段内的逗号和换行。
如果数据里还有双引号,接收方需要支持转义解析。大多数现代 CSV 库都支持,但如果你是自己写正则去解析 CSV,十个有九个会在这翻车。所以建议接收方用成熟的 CSV 解析库,别自己造轮子。
3.4 生成其他系统可导入的数据文件
除了给人看的数据报表,INTO OUTFILE 更常见的价值是给机器消费。比如把 MySQL 数据导成文本文件,再灌入大数据平台。
给 ClickHouse 导数据,通常导成 Tab 分隔格式:
SELECT id, name, amount INTO OUTFILE '/data/clickhouse_import.txt' CHARACTER SET utf8mb4 FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n' FROM orders;给 Hadoop Hive 导数据,通常用\x01控制符做字段分隔,因为业务数据里几乎不可能出现这个字符:
SELECT id, name, amount INTO OUTFILE '/data/hive_import.txt' CHARACTER SET utf8mb4 FIELDS TERMINATED BY '\x01' LINES TERMINATED BY '\n' FROM orders;这些格式在对应的导入工具里都有成熟的配置方式。INTO OUTFILE 在这里只是数据出口的第一步,但也是最重要的一步——格式定对了,后面导入流程顺畅无比;格式定错了,数据对账能对到怀疑人生。
4. 踩坑实录:INTO OUTFILE 最常见的 7 个问题
4.1 ERROR 1290:secure_file_priv 把路堵死了
报错长这样:
ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement这个错误大概是 INTO OUTFILE 最常见的报错。核心原因就是secure_file_priv限制了导出目录,而你写的目标路径不在允许范围内。
排查步骤:
- 查看当前策略:
SHOW VARIABLES LIKE 'secure_file_priv'; - 如果值为具体目录,把导出路径改成该目录下。
- 如果必须写到其他目录,修改配置文件后重启实例。
曾经有个项目,业务方要求把文件生成到应用服务器的共享目录里,但 MySQL 在独立数据库服务器上,secure_file_priv是/var/lib/mysql-files/,两边不在同一台机器。最后我的方案是:先导出到/var/lib/mysql-files/,再用部署脚本把文件定时同步到应用服务器的共享目录。绕开限制,比硬改数据库配置更稳妥。
4.2 文件已存在报错,明明目录有权限也不行
ERROR 1086 (HY000): File '/var/lib/mysql-files/user_export.csv' already existsINTO OUTFILE 有个反直觉的特性:不会覆盖已存在的文件。哪怕文件是空的,只要存在就报错。
解决方法是先删除旧文件再执行导出:
rm -f /var/lib/mysql-files/user_export.csv自动化脚本里建议把这个逻辑写进去,比如:
rm -f /data/export/order_$(date +%Y%m%d).csv mysql -e "SELECT ... INTO OUTFILE ..."另外,MySQL 8.0.19 引入了一个可选的关键字,支持覆盖已有文件:
SELECT ... INTO OUTFILE '/path/file.csv' ...如果你用的版本支持,可以查阅对应版本文档确认用法。生产环境我仍然推荐先删除再导出,逻辑更清晰,也便于和告警、日志联动。
4.3 NULL 值导出来变成 \N 或干脆空掉
默认行为下,NULL 值导出为\N。如果你用了ENCLOSED BY '"',NULL 值在某些版本下又会变成空字符串。这两种表现都容易让下游解析困惑。
我处理 NULL 的习惯是:不要依赖导出参数去猜,而是在 SQL 里明确处理。用IFNULL把 NULL 显式替换成想要的默认值,比如空字符串、0、'1970-01-01',或者业务上约定的其他值。这样无论导出参数怎么变,文件内容都是确定的。
比如金额字段,NULL 表示未支付,导出时想变成 0:
SELECT order_id, IFNULL(amount, 0) AS amount INTO OUTFILE '/tmp/order.csv' ...要不要保留 NULL 语义,要看接收方的数据模型。接收方数据库字段允许为 NULL,就保持\N,导入时统一处理;接收方字段非空,就直接IFNULL替换掉。这个决定应该在设计导出规则时明确写下来,避免每张表处理方式不一致。
4.4 UTF-8 导出的 CSV 用 Excel 打开乱码
现象:用 Notepad 打开文件一切正常,用 Excel 打开乱码。
原因:Excel 默认按系统本地编码(Windows 下是 ANSI,即 GBK)打开 CSV 文件,而 MySQL 导出的文件是 UTF-8 编码,两边对不上。
解决办法有三种:
第一种,导出成 GBK:
SELECT ... INTO OUTFILE '/tmp/user.csv' CHARACTER SET gbk ...第二种,导出成 UTF-8 后手动转码:
iconv -f UTF-8 -t GBK /tmp/user.csv > /tmp/user_gbk.csv第三种,不折腾编码,直接让 Excel 用 UTF-8 打开。可以先打开 Excel,在“数据”选项卡里选择“从文本/CSV 导入”,手动指定文件编码为 UTF-8,再完成导入。新版 Excel 已经能识别带 BOM 的 UTF-8 文件,也可以考虑在文件头加 BOM。
实际项目看接收方是谁。给数据分析师用,他们一般用 Python 或者 Excel 的文本导入功能,UTF-8 没问题。给业务同事用,他们更习惯双击打开,那就直接导 GBK 或提供转码后的文件。
4.5 大表导出把库拖慢、主从延迟飙升
几百万行的表导出,对 MySQL 实例的压力不可忽视。SELECT 本身如果是快照读,在 InnoDB 下不会阻塞正常的 DML,但导出过程中会占大量 IO 和临时表空间,主从复制也可能因为 binlog 或临时空间产生延迟。
我的经验是给导出任务设几个红线:
- 大表导出尽量放在从库上执行,不占用主库资源。
- 必须从主库导出时,选择业务低峰期,并做好监控。
- 如果导出数据量极大,考虑分批导出,按主键范围或时间范围切分,避免单条 SELECT 跑太久。
- 大导出别在事务里包着,更不要在这个 SELECT 上加 FOR UPDATE,否则就是给自己找麻烦。
曾经做过一次千万级用户表的全量导出,开始直接在主库跑,结果业务方反馈高峰期接口变慢。后来改成夜间在只读从库导出,问题立刻消失。这个经验现在每次给别人讲 INTO OUTFILE 我都会提一句:工具本身没问题,但执行位置和时机选错了,再好的工具也能变成事故。
4.6 字段里的换行和引号让数据错位
这个坑最容易出现在备注、地址、JSON 字符串这类自由文本字段中。字段值里包含换行符,导出后一行数据被拆成两行;包含双引号,又和ENCLOSED BY的包裹符冲突。
设置转义符可以缓解引号问题,但换行符如果转义成\n,接收方解析时处理不对,照样错位。
我的解决方案分两步:
首先,在 SQL 里做预处理。对自由文本字段进行清洗,把字段内的换行替换成空格或者\\n文本:
SELECT id, REPLACE(REPLACE(remark, '\r', ' '), '\n', ' ') AS remark INTO OUTFILE '/tmp/user.csv' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' FROM users;然后,在接收方约定:所有字符串字段都可能有引号包裹,字符串里的双引号以\"形式出现,要按照 CSV 标准处理。这样双方都按规范走,数据错位问题基本就根除了。
4.7 导出的文件权限和属主问题
Linux 环境下,INTO OUTFILE 生成的文件属主是 MySQL 运行用户(通常是 mysql),默认权限可能比较严格。如果后续脚本需要用另一个系统用户读取这个文件,可能遇到Permission denied。
遇到这种情况,要么把导出目录的组权限放开,要么在导出后加一步 chown 或 chmod:
chown appuser:appuser /data/export/order.csv chmod 644 /data/export/order.csv自动化脚本里建议在导出语句执行后追加权限调整命令,避免手动操作遗漏。这个点看似小,但在跨部门协作、多个系统账号共用服务器的情况下,卡你一两天都有可能。
5. 它和 mysqldump、客户端重定向到底选哪个
5.1 一句话总结三者的差异
mysqldump是逻辑备份工具,导出的是 SQL 语句文件,包含建表语句和 INSERT 语句,适合数据库备份、库表迁移。它不是纯数据文件,恢复时需要重新执行 SQL。
SELECT INTO OUTFILE是数据导出功能,导出的是纯文本数据文件,没有建表语句,格式可控性强,适合数据交换、ETL、对接外部系统。
客户端重定向是一种取巧方式,典型写法是:
mysql -e "SELECT * FROM users" > /tmp/user.txt数据先回传到客户端,再写入客户端本地文件。优点是简单,能把数据直接导出到本机;缺点是格式不可控、大数据量慢、容易受客户端字符集影响。
三者对比:
| 对比项 | mysqldump | INTO OUTFILE | 客户端重定向 |
|---|---|---|---|
| 导出位置 | 客户端本机 | 服务器本地 | 客户端本机 |
| 内容形式 | SQL 语句 | 纯文本数据 | 纯文本数据 |
| 格式控制 | 弱 | 强 | 弱 |
| 包含表结构 | 包含 | 不包含 | 不包含 |
| 大数据量速度 | 中 | 快 | 慢 |
| 典型用途 | 备份、迁移 | 数据交付、ETL | 临时导出 |
5.2 我常用的选型建议
实际项目里,我的选择逻辑很简单:
- 要备份整库或整表,用 mysqldump。
- 要导数据给别的系统用,字段格式有严格要求,用 INTO OUTFILE。
- 就临时看一眼数据,或者数据量很小,用客户端重定向足够。
- 如果想导出后直接下载到本地电脑,优先考虑客户端重定向或者 INTO OUTFILE 导出后再拉取文件。
有一个场景特别推荐 INTO OUTFILE:MySQL 数据迁移到大数据平台。数据量大、格式要求严格、需要可重复执行,INTO OUTFILE 配合对端平台的导入命令,是最成熟的链路之一。我曾经做过 MySQL 到 Hive 的离线同步,每天几千万条数据,凌晨定时导出成\x01分隔的文本文件,再用上层调度系统触发 Hive 的 LOAD DATA,稳定运行了大半年没出问题。
如果你现在的导出还是靠 SELECT 一条条查出来拼接文件,认真考虑一下 INTO OUTFILE。它的学习成本很低,不过就是一个语句尾部加参数的事,但带来的效率提升是实打实的。
我个人在实际操作中的体会是:INTO OUTFILE 最大的价值不只是导出快,而是它把“数据格式的控制权”真正交到了 SQL 手里。分隔符、引号、转义、换行、编码,全部在一条语句里定义清楚,形成规范之后几乎零维护成本。最后再分享一个小技巧:给所有导出任务建一个独立的导出目录,按日期分文件,配合定时清理脚本,这样既能避免文件已存在的报错,也方便回溯历史数据。后续如果业务量增长,还可以在这个目录上挂监控和告警,做到导出任务无人值守。