Docker MySQL 自动备份与恢复教程(systemd 定时任务)
本文介绍如何为 Docker 中运行的 MySQL 数据库建立自动备份方案:
- 使用
mysqldump导出数据库; - 自动压缩为
.sql.gz; - 每 5 天备份一次;
- 自动保留最近 30 天的备份;
- 使用
systemd timer设置开机自动启用; - 提供完整的数据库恢复流程。
本文统一使用MyProject作为示例项目代号。实际使用时,只需要替换项目名称、容器名、数据库名、密码和目录即可。
1. 需要替换的配置
请根据自己的服务器环境修改以下名称:
| 示例名称 | 说明 |
|---|---|
MyProject | 项目名称或项目代号 |
myproject | 项目名称的小写形式 |
myproject-mysql | MySQL Docker 容器名称 |
myproject-backend | 后端 Docker 容器名称 |
myproject123456 | MySQL root 密码 |
/root/MyProject_Backup/mysql | 备份保存目录 |
/root/backup_myproject_mysql.sh | 自动备份脚本路径 |
先查看正在运行的 Docker 容器:
dockerps--format"table {{.Names}}\t{{.Status}}"输出示例:
NAMES STATUS myproject-mysql Up 10 days myproject-backend Up 10 days2. 为什么不要直接复制 MySQL 数据目录
MySQL 数据通常挂载在宿主机目录中,例如:
/root/MyProject_Web/data/mysql/不要在 MySQL 正在运行时直接复制这个目录:
cp-a/root/MyProject_Web/data/mysql /root/mysql_backup因为 MySQL 在运行过程中会持续写入表数据、日志和索引文件。直接复制可能产生不一致的数据副本,恢复时可能失败。
更可靠的方法是使用:
mysqldump将数据库导出为 SQL 文件,再压缩为:
.sql.gz这种备份方式可以在 MySQL 正常运行时执行,且更容易迁移、检查和恢复。
3. 创建独立备份目录
建议将备份保存在项目目录之外,避免项目目录被误删时连备份一起丢失。
mkdir-p/root/MyProject_Backup/mysqlchmod700/root/MyProject_Backup/mysql以后生成的备份文件会保存在:
/root/MyProject_Backup/mysql/文件名示例:
myproject_2026-06-26_200000.sql.gz4. 创建 MySQL 自动备份脚本
创建脚本文件:
vi/root/backup_myproject_mysql.sh将下面内容完整粘贴进去:
#!/usr/bin/env bashset-euopipefail# ========================================# MyProject Docker MySQL 自动备份脚本# ========================================# Docker 中 MySQL 容器名称CONTAINER_NAME="myproject-mysql"# 要备份的数据库名称DATABASE_NAME="myproject"# MySQL root 密码MYSQL_ROOT_PASSWORD="myproject123456"# 备份保存目录BACKUP_DIR="/root/MyProject_Backup/mysql"# 保留最近多少天的备份# 每 5 天备份一次,保留 30 天约等于保留 6 份RETENTION_DAYS=30# 创建备份目录mkdir-p"${BACKUP_DIR}"chmod700"${BACKUP_DIR}"# 检查 MySQL 容器是否正在运行if!dockerps--format'{{.Names}}'|grep-qx"${CONTAINER_NAME}";thenecho"[$(date'+%F %T')] 错误:MySQL 容器${CONTAINER_NAME}未运行。">&2exit1fi# 生成带时间戳的备份文件名TIMESTAMP=$(date+"%F_%H%M%S")BACKUP_FILE="${BACKUP_DIR}/${DATABASE_NAME}_${TIMESTAMP}.sql.gz"TMP_FILE="${BACKUP_FILE}.tmp"echo"[$(date'+%F %T')] 开始备份数据库:${DATABASE_NAME}"# 导出数据库并进行 gzip 压缩# --single-transaction:InnoDB 数据库可在不停机状态下进行一致性备份# --quick:减少大数据库备份时的内存占用dockerexec\-eMYSQL_PWD="${MYSQL_ROOT_PASSWORD}"\"${CONTAINER_NAME}"\mysqldump\-uroot\--single-transaction\--quick\--routines\--events\--triggers\--databases"${DATABASE_NAME}"\|gzip-c>"${TMP_FILE}"# 检查压缩包是否完整gzip-t"${TMP_FILE}"# 验证成功后,将临时文件改为正式备份文件mv"${TMP_FILE}""${BACKUP_FILE}"echo"[$(date'+%F %T')] 备份成功:${BACKUP_FILE}"# 删除超过保留期限的旧备份find"${BACKUP_DIR}"\-typef\-name"*.sql.gz"\-mtime+"${RETENTION_DAYS}"\-deleteecho"[$(date'+%F %T')] 已清理超过${RETENTION_DAYS}天的旧备份。"保存并退出vi:
按 Esc 输入 :wq 按 Enter为脚本添加执行权限:
chmod700/root/backup_myproject_mysql.sh检查权限:
ls-l/root/backup_myproject_mysql.sh正常应看到类似:
-rwx------ 1 root root ... /root/backup_myproject_mysql.sh其中x表示脚本具有执行权限。
5. 手动测试备份
先手动运行一次脚本,确认配置正确:
/root/backup_myproject_mysql.sh正常情况下会输出类似:
[2026-06-26 20:00:00] 开始备份数据库:myproject [2026-06-26 20:00:01] 备份成功:/root/MyProject_Backup/mysql/myproject_2026-06-26_200000.sql.gz [2026-06-26 20:00:01] 已清理超过 30 天的旧备份。查看备份文件:
ls-lh/root/MyProject_Backup/mysql/正常会看到:
myproject_2026-06-26_200000.sql.gz建议先确认手动备份成功,再配置自动定时任务。
6. 创建 systemd 服务
创建service文件:
cat>/etc/systemd/system/myproject-mysql-backup.service<<'EOF' [Unit] Description=MyProject MySQL Backup After=docker.service Requires=docker.service [Service] Type=oneshot ExecStart=/root/backup_myproject_mysql.sh EOF查看服务文件:
cat/etc/systemd/system/myproject-mysql-backup.service正常应显示:
[Unit] Description=MyProject MySQL Backup After=docker.service Requires=docker.service [Service] Type=oneshot ExecStart=/root/backup_myproject_mysql.sh7. 创建每 5 天执行一次的 systemd 定时器
创建 timer 文件:
cat>/etc/systemd/system/myproject-mysql-backup.timer<<'EOF' [Unit] Description=Run MyProject MySQL backup every 5 days [Timer] # 定时器启动后,每隔 5 天执行一次 OnUnitActiveSec=5d # 服务器关机期间错过任务时,恢复运行后补做 Persistent=true [Install] WantedBy=timers.target EOF参数说明:
OnUnitActiveSec=5d表示定时器启动后,每隔 5 天运行一次备份服务。
Persistent=true表示若服务器在计划执行时关机,系统恢复后会补做错过的备份任务。
8. 启用定时器并设置开机自动启动
执行:
systemctl daemon-reload systemctlenable--nowmyproject-mysql-backup.timer systemctl restart myproject-mysql-backup.timer检查定时器状态:
systemctl status myproject-mysql-backup.timer正常会显示类似:
Loaded: loaded (...; enabled; ...) Active: active (waiting) Trigger: ...其中:
enabled:服务器重启后,定时器会自动启动;active (waiting):定时器正在等待下一次执行;Trigger:下一次自动备份的时间。
查看下次运行时间:
systemctl list-timers--all|grepmyproject-mysql-backup检查是否已设置为开机启动:
systemctl is-enabled myproject-mysql-backup.timer正常应输出:
enabled9. 测试 systemd 是否能正常调用备份脚本
手动触发一次 systemd 备份:
systemctl start myproject-mysql-backup.service查看日志:
journalctl-umyproject-mysql-backup.service-n30--no-pager正常会看到类似:
Starting MyProject MySQL Backup... 开始备份数据库:myproject 备份成功:/root/MyProject_Backup/mysql/myproject_2026-06-26_200000.sql.gz 已清理超过 30 天的旧备份。 Finished MyProject MySQL Backup.这说明 systemd 可以正常调用备份脚本。
10. 常用备份管理命令
查看所有备份文件
ls-lh/root/MyProject_Backup/mysql/手动立即备份
/root/backup_myproject_mysql.sh或:
systemctl start myproject-mysql-backup.service查看自动备份状态
systemctl status myproject-mysql-backup.timer查看下一次备份时间
systemctl list-timers--all|grepmyproject-mysql-backup查看备份日志
journalctl-umyproject-mysql-backup.service-n50--no-pager停止自动备份
systemctl disable--nowmyproject-mysql-backup.timer重新启用自动备份
systemctlenable--nowmyproject-mysql-backup.timer11. 如何恢复某一份数据库备份
假设需要恢复以下备份:
/root/MyProject_Backup/mysql/myproject_2026-06-26_200000.sql.gz恢复会将当前数据库替换为历史备份版本。开始前请确认选中的备份文件正确。
11.1 先备份当前数据库
恢复前先生成一份当前数据库备份:
/root/backup_myproject_mysql.sh这样即使恢复了错误版本,也可以回退到恢复操作前的状态。
11.2 停止项目后端容器
先查看运行中的容器:
dockerps假设后端容器名称为:
myproject-backend停止后端:
dockerstop myproject-backend
myproject-backend是示例名称,请替换为实际后端容器名称。
MySQL 容器myproject-mysql不需要停止。
11.3 删除当前数据库
dockerexec-it\-eMYSQL_PWD="myproject123456"\myproject-mysql\mysql-uroot-e"DROP DATABASE IF EXISTS myproject;"这条命令会删除当前的myproject数据库,因此必须确认前一步的备份已成功完成。
11.4 导入指定备份
gunzip-c/root/MyProject_Backup/mysql/myproject_2026-06-26_200000.sql.gz\|dockerexec-i\-eMYSQL_PWD="myproject123456"\myproject-mysql\mysql-uroot导入过程可能没有额外输出;只要没有报错,一般表示恢复成功。
11.5 检查恢复结果
查看数据库:
dockerexec-it\-eMYSQL_PWD="myproject123456"\myproject-mysql\mysql-uroot-e"SHOW DATABASES;"查看恢复后的表:
dockerexec-it\-eMYSQL_PWD="myproject123456"\myproject-mysql\mysql-uroot-e"USE myproject; SHOW TABLES;"能够正常显示表名,通常说明恢复成功。
11.6 启动后端容器
dockerstart myproject-backend将myproject-backend替换成实际后端容器名称。
12. 完整恢复流程
# 1. 备份恢复前的当前数据库/root/backup_myproject_mysql.sh# 2. 查看所有备份文件,确认需要恢复哪一份ls-lh/root/MyProject_Backup/mysql/# 3. 停止项目后端容器dockerstop myproject-backend# 4. 删除当前数据库dockerexec-it\-eMYSQL_PWD="myproject123456"\myproject-mysql\mysql-uroot-e"DROP DATABASE IF EXISTS myproject;"# 5. 恢复指定备份gunzip-c/root/MyProject_Backup/mysql/myproject_2026-06-26_200000.sql.gz\|dockerexec-i\-eMYSQL_PWD="myproject123456"\myproject-mysql\mysql-uroot# 6. 检查恢复后的数据表dockerexec-it\-eMYSQL_PWD="myproject123456"\myproject-mysql\mysql-uroot-e"USE myproject; SHOW TABLES;"# 7. 启动项目后端容器dockerstart myproject-backend13. 备份策略总结
| 项目 | 设置 |
|---|---|
| MySQL 容器 | myproject-mysql |
| 数据库名称 | myproject |
| 备份目录 | /root/MyProject_Backup/mysql |
| 备份脚本 | /root/backup_myproject_mysql.sh |
| 自动备份频率 | 每 5 天一次 |
| 备份保留时间 | 30 天 |
| 预计保留份数 | 约 6 份 |
| 备份格式 | .sql.gz |
| 自动删除旧备份 | 是 |
| 开机自动启用定时器 | 是 |
14. 进一步建议
本文中的备份文件仍然存放在同一台服务器,因此可以防止:
- 数据库误删;
- 错误更新或错误覆盖;
- 项目升级失败;
- 项目目录被误删;
- 数据库表被错误修改。
但它无法防止:
- 服务器硬盘损坏;
- 整台服务器不可用;
- 云服务器被删除;
- 勒索软件或严重系统故障。
对于重要数据库,建议定期将以下目录同步到另一块硬盘、NAS、对象存储或另一台服务器:
/root/MyProject_Backup/mysql/