最轻量可控的MySQL容器备份方式是mysqldump + docker exec,需加--single-transaction和--routines保证一致性及存储过程导出,密码应存于600权限的~/.my.cnf,分库备份优于管道压缩,脚本须检测容器状态与磁盘空间,还原前需手动建库指定字符集,且必须验证还原有效性。

直接用 mysqldump + docker exec 是最轻量、最可控的方式,适合 90% 的中小规模 MySQL 容器场景。别一上来就折腾 XtraBackup 或挂载数据卷——除非你有 TB 级数据或严格 RPO/RTO 要求。
备份命令必须加 --single-transaction 和 --routines
不加这两个参数,备份可能不一致或漏掉存储过程/函数:
-
--single-transaction对 InnoDB 表开启一致性快照,全程不锁表(MyISAM 仍会锁) -
--routines确保导出CREATE PROCEDURE/FUNCTION语句,否则还原后逻辑缺失 - 如果用了视图或事件,还得补上
--events和--triggers - 密码明文写在命令里极不安全,应改用配置文件:在宿主机建
~/.my.cnf,设权限chmod 600 ~/.my.cnf,内容为:
[client] user = root password = your_real_password
然后命令简化为:docker exec -i mysql-container mysqldump --single-transaction --routines --all-databases > backup.sql
压缩和分库备份要分开做,别混在一条管道里
看似“一行流式压缩”很酷,但实际容易失败且无法调试:
- 错误如
mysqldump: Got error: 2013: Lost connection to MySQL server during query在管道中会被吞掉,你只看到空文件 - 分库备份时,
mysqldump每个库单独执行更稳,也方便后续按需还原单库 - 推荐做法:先 dump 到临时文件(
/tmp/backup_$$),再gzip,最后mv到归档目录 - 压缩用
gzip -1(最快)或zstd -T1(比 gzip 快 3 倍,需容器内装 zstd)
定时备份脚本必须检查容器状态和磁盘空间
crontab 不会告诉你容器已退出或 /backup 分区只剩 200MB:
- 开头加检测:
if ! docker ps --format '{{.Names}}' | grep -q '^mysql-container$'; then echo 'container down'; exit 1; fi - 加磁盘空间检查:
if [ $(df /backup | tail -1 | awk '{print $5}' | sed 's/%//') -gt 90 ]; then echo 'disk full'; exit 1; fi - 备份文件名务必含时间戳和容器名,例如:
mysql-container_20260702_0920_all.sql.gz,避免覆盖 - 保留最近 7 天备份:
find /backup -name 'mysql-container_*.sql.gz' -mtime +7 -delete
还原时别直接 mysql < backup.sql 就完事
全库 SQL 文件里包含 DROP DATABASE 和 CREATE DATABASE,但默认字符集可能不匹配,导致中文乱码或建库失败:
- 先查原库字符集:
docker exec mysql-container mysql -Nse "SELECT DEFAULT_CHARACTER_SET_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='your_db'" - 还原前手动创建库并指定字符集:
docker exec mysql-container mysql -e "CREATE DATABASE IF NOT EXISTS your_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" - 再导入:
zcat backup.sql.gz | docker exec -i mysql-container mysql your_db - 若备份含
--all-databases,则必须用mysql(不是mysql your_db),且确保目标实例无同名库残留
真正麻烦的从来不是“怎么备份”,而是“怎么确认这次备份真能还原”。每次新脚本上线,务必拿测试库跑一次完整 restore 流程——跳过这步,等于没备。


















