MySQL 的“导入导出”其实对应几种不同需求:迁移完整数据库、备份后恢复、交换 CSV 文件,或者只导出满足条件的一部分数据。先选对工具,通常比给命令加一堆参数更重要。
本文示例使用以下变量:
主机:127.0.0.1
端口:3306
数据库:app_db
用户:backup
命令中的 -p 会交互式提示输入密码,不要把真实密码直接写在命令行或脚本里。
先看怎么选
| 场景 | 导出方式 | 导入方式 |
|---|---|---|
| 完整迁移、日常备份 | mysqldump | mysql |
| 迁移单个表或部分行 | mysqldump --where | mysql |
| 与 Excel、Python 等工具交换文件 | SELECT ... INTO OUTFILE | LOAD DATA |
| 已有 CSV/TSV 文件 | 文件本身 | LOAD DATA 或 mysqlimport |
| 数据量很大,需要并行导入导出 | MySQL Shell Dump Utility | MySQL Shell util.loadDump() |
如果目标是“把数据库完整搬到另一台 MySQL”,优先使用 SQL dump;如果目标是“给其他程序一个文件”,使用 CSV/TSV 更合适。
使用 mysqldump 导出 SQL
导出一个数据库
mysqldump \
-h 127.0.0.1 \
-P 3306 \
-u backup \
-p \
--single-transaction \
--routines \
--events \
--triggers \
--default-character-set=utf8mb4 \
app_db > app_db.sql
这会把表结构和表数据写成 SQL 文件。--routines、--events 和 --triggers 用于显式包含存储过程、事件和触发器,避免迁移后只剩下表和数据。
对主要使用 InnoDB 的库,--single-transaction 可以在不长时间锁表的情况下获得一致性快照。它不等于对所有存储引擎都提供一致性:MyISAM 等非事务表仍应安排停写、锁表或使用其他备份方案;导出过程中也不要同时执行会改变表结构的 DDL。
导出多个数据库或全部数据库
# 导出指定的多个数据库,同时写入 CREATE DATABASE 和 USE
mysqldump -h 127.0.0.1 -u backup -p \
--databases app_db audit_db > databases.sql
# 导出实例中的全部数据库
mysqldump -h 127.0.0.1 -u backup -p \
--all-databases > all-databases.sql
--databases 和 --all-databases 生成的文件通常自带创建数据库和切换数据库的语句。单数据库命令 mysqldump app_db 则不会自动创建目标数据库,导入时需要手动指定数据库。
只导出表结构或只导出数据
# 只导出表结构,不导出行数据
mysqldump -h 127.0.0.1 -u backup -p \
--no-data app_db > app_db-schema.sql
# 只导出数据,不导出 CREATE TABLE
mysqldump -h 127.0.0.1 -u backup -p \
--no-create-info app_db > app_db-data.sql
这两个选项适合测试环境初始化,或者需要先手工调整表结构、再加载数据的场景。
只导出指定的表
mysqldump -h 127.0.0.1 -u backup -p \
--single-transaction app_db users orders > selected-tables.sql
表名写在数据库名后面即可。导出视图、触发器或其他对象时,要根据迁移范围显式检查对应选项和权限。
按条件导出部分数据
mysqldump -h 127.0.0.1 -u backup -p \
--single-transaction \
app_db orders \
--where="created_at >= '2026-01-01'" > orders-2026.sql
--where 会把条件附加到导出数据的查询中。条件中的日期、状态等值应来自可信配置,并在执行前确认不会误导出或漏导数据。
压缩导出文件
mysqldump -h 127.0.0.1 -u backup -p \
--single-transaction app_db | gzip > app_db.sql.gz
恢复时直接解压到 mysql 命令即可,不必先生成中间文件:
gzip -dc app_db.sql.gz | mysql -h 127.0.0.1 -u backup -p app_db
导入 SQL 文件
导入到已有数据库
mysql -h 127.0.0.1 -P 3306 -u backup -p app_db < app_db.sql
如果 app_db 还不存在,先创建数据库,并明确指定字符集:
mysql -h 127.0.0.1 -u root -p \
-e "CREATE DATABASE IF NOT EXISTS app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;"
mysql -h 127.0.0.1 -u backup -p app_db < app_db.sql
也可以登录 MySQL 后执行:
CREATE DATABASE IF NOT EXISTS app_db
CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
USE app_db;
SOURCE /backup/app_db.sql;
如果 dump 文件是用 --databases 或 --all-databases 生成的,文件里已经包含 CREATE DATABASE 和 USE,导入时可以不指定默认数据库:
mysql -h 127.0.0.1 -u root -p < all-databases.sql
从一台服务器直接传到另一台
不想落地中间文件时,可以使用管道:
mysqldump -h source.example.com -u backup -p \
--single-transaction app_db \
| mysql -h target.example.com -u backup -p app_db
两端的 MySQL 版本、字符集、SQL mode 和表结构应提前确认。生产环境迁移建议先导入临时数据库,完成校验后再切换业务连接,避免导入失败后留下半成品数据。
导出和导入 CSV/TSV
SQL dump 适合 MySQL 到 MySQL;如果需要给其他程序处理,通常导出为 CSV 或 TSV。
使用 SELECT INTO OUTFILE 导出
SELECT id, name, created_at
INTO OUTFILE '/var/lib/mysql-files/users.csv'
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM users
ORDER BY id;
这里有几个容易忽略的事实:
- 文件写在 MySQL 服务端主机上,不是执行
mysql命令的客户端机器。 - 执行账号需要
FILE权限,且路径通常受secure_file_priv限制。 - MySQL 不会覆盖已经存在的目标文件;重复导出前应换一个新文件名或清理旧文件。
- 导出列最好显式写出,并加上
ORDER BY,不要在稳定脚本中使用SELECT *。
如果只想把查询结果保存到当前客户端机器,也可以使用客户端重定向:
mysql -h 127.0.0.1 -u backup -p -D app_db \
--batch --raw \
-e "SELECT id, name, created_at FROM users ORDER BY id" \
> users.tsv
这种方式输出的是制表符分隔文本,适合简单交换;需要严格控制 CSV 引号和换行时,优先使用 INTO OUTFILE 的格式选项。
使用 LOAD DATA 导入
假设 users.csv 第一行是表头,字段顺序为 id,name,created_at:
LOAD DATA LOCAL INFILE '/path/to/users.csv'
INTO TABLE users
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 LINES
(id, name, @created_at)
SET created_at = NULLIF(@created_at, '');
LOCAL 表示文件位于客户端主机。Windows 导出的文件通常使用 \r\n 换行,此时应把 LINES TERMINATED BY '\n' 改成 LINES TERMINATED BY '\r\n'。
MySQL 8.x 默认可能关闭本地文件加载,需要客户端和服务端同时允许:
mysql --local-infile=1 -h 127.0.0.1 -u backup -p app_db
服务端可检查当前配置:
SHOW VARIABLES LIKE 'local_infile';
只对可信文件和可信服务器开启 LOCAL。不需要批量导入时,保持关闭更安全;如果确实要开启,导入完成后应恢复原来的设置。
使用 mysqlimport
mysqlimport 是 LOAD DATA 的命令行封装。它会根据文件名(去掉扩展名)寻找目标表,所以 users.csv 默认对应 users 表:
mysqlimport \
--local \
--ignore-lines=1 \
--columns=id,name,created_at \
--fields-terminated-by=',' \
--fields-optionally-enclosed-by='"' \
--lines-terminated-by='\n' \
-h 127.0.0.1 -u backup -p \
app_db users.csv
字段分隔符、包围符和换行符必须与文件实际格式一致,否则常见结果是列错位、中文乱码或整行导入失败。
字符集和权限
统一使用 utf8mb4
导入导出前可以检查连接和服务端字符集:
SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';
实践中建议统一以下几层:
- 数据库和表使用
utf8mb4。 mysqldump增加--default-character-set=utf8mb4。- CSV 导入导出明确写出
CHARACTER SET utf8mb4。 - 连接客户端、应用程序和终端也使用 UTF-8。
不要用 latin1 去“修复”乱码。乱码通常是数据已经被错误解码或重复转码,先确认原文件编码和连接字符集,再决定是否转换。
导出账号不等于导出数据库
普通的 mysqldump app_db 主要导出数据库对象和数据,不会自动把业务账号、密码和授权关系迁移到目标实例。迁移前至少检查:
SHOW GRANTS FOR 'backup'@'%';
账号和授权应在目标环境按最小权限重新创建,不要为了导入方便直接给业务用户 ALL PRIVILEGES 或使用 root。
导入后的校验
命令执行成功只是第一步,至少做一次行数和关键范围校验:
SELECT COUNT(*) AS row_count, MIN(id) AS min_id, MAX(id) AS max_id
FROM users;
SELECT COUNT(*) AS row_count, MIN(created_at) AS first_created_at,
MAX(created_at) AS last_created_at
FROM orders;
还应抽查:
- 关键表的行数是否与源库一致;
- 主键、唯一键和外键是否正常;
- 中文、emoji、时区和
NULL是否保持原样; - 视图、触发器、事件和存储过程是否存在;
- 应用是否能用目标账号完成读写;
- 压缩文件是否可以正常读取:
gzip -t app_db.sql.gz。
不要随意使用 mysql --force 忽略导入错误。它可能让命令看起来执行完成,但数据库已经处于部分导入状态;正确做法是保留错误输出,修复原因后重新导入或丢弃临时数据库重来。
大数据量场景
mysqldump 简单可靠,几十 GB 以上的库则需要关注磁盘、网络和导入时长。可以先尝试压缩管道;如果仍然太慢,再使用 MySQL Shell 的 Dump Utility,它支持并行导出、分片文件、压缩和并行加载:
// 在源库连接上执行
util.dumpSchemas(["app_db"], "/backup/app_db", { threads: 4 });
// 在目标库连接上执行
util.loadDump("/backup/app_db", { threads: 4 });
并行不等于可以忽略一致性和资源上限。执行前确认备份目录为空、目标实例有足够磁盘空间,并观察 CPU、磁盘 IO、连接数和 InnoDB 日志压力。数据一致性仍取决于表使用的存储引擎和执行期间的写入、DDL 行为。
一套可复用的迁移流程
# 1. 导出
mysqldump -h source.example.com -u backup -p \
--single-transaction \
--routines --events --triggers \
--default-character-set=utf8mb4 \
app_db | gzip > app_db.sql.gz
# 2. 检查压缩文件
gzip -t app_db.sql.gz
# 3. 创建临时数据库
mysql -h target.example.com -u root -p \
-e "CREATE DATABASE app_db_import CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;"
# 4. 导入临时数据库
gzip -dc app_db.sql.gz | \
mysql -h target.example.com -u backup -p app_db_import
导入后完成行数、关键业务数据和应用连接校验,再安排切换。不要把“生成了一个 .sql 文件”当成备份完成:没有经过恢复测试的备份,不能证明真的可恢复。
总结
- MySQL 到 MySQL 的完整迁移,使用
mysqldump配合mysql。 - 只要表主要是 InnoDB,就优先考虑
--single-transaction。 - 交换 CSV/TSV 时,使用
SELECT ... INTO OUTFILE和LOAD DATA,并明确字符集与换行符。 LOAD DATA LOCAL有安全边界,只对可信连接和可信文件开启。- 大数据量优先考虑压缩管道,仍不够时再使用 MySQL Shell 的并行工具。
- 导入后必须校验;生产迁移优先导入临时库,确认无误后再切换。