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 容器:
bash
docker ps --format "table {{.Names}}\t{{.Status}}"
输出示例:
text
NAMES STATUS
myproject-mysql Up 10 days
myproject-backend Up 10 days
2. 为什么不要直接复制 MySQL 数据目录
MySQL 数据通常挂载在宿主机目录中,例如:
text
/root/MyProject_Web/data/mysql/
不要在 MySQL 正在运行时直接复制这个目录:
bash
cp -a /root/MyProject_Web/data/mysql /root/mysql_backup
因为 MySQL 在运行过程中会持续写入表数据、日志和索引文件。直接复制可能产生不一致的数据副本,恢复时可能失败。
更可靠的方法是使用:
bash
mysqldump
将数据库导出为 SQL 文件,再压缩为:
text
.sql.gz
这种备份方式可以在 MySQL 正常运行时执行,且更容易迁移、检查和恢复。
3. 创建独立备份目录
建议将备份保存在项目目录之外,避免项目目录被误删时连备份一起丢失。
bash
mkdir -p /root/MyProject_Backup/mysql
chmod 700 /root/MyProject_Backup/mysql
以后生成的备份文件会保存在:
text
/root/MyProject_Backup/mysql/
文件名示例:
text
myproject_2026-06-26_200000.sql.gz
4. 创建 MySQL 自动备份脚本
创建脚本文件:
bash
vi /root/backup_myproject_mysql.sh
将下面内容完整粘贴进去:
bash
#!/usr/bin/env bash
set -euo pipefail
# ========================================
# 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}"
chmod 700 "${BACKUP_DIR}"
# 检查 MySQL 容器是否正在运行
if ! docker ps --format '{{.Names}}' | grep -qx "${CONTAINER_NAME}"; then
echo "[$(date '+%F %T')] 错误:MySQL 容器 ${CONTAINER_NAME} 未运行。" >&2
exit 1
fi
# 生成带时间戳的备份文件名
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:减少大数据库备份时的内存占用
docker exec \
-e MYSQL_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}" \
-type f \
-name "*.sql.gz" \
-mtime +"${RETENTION_DAYS}" \
-delete
echo "[$(date '+%F %T')] 已清理超过 ${RETENTION_DAYS} 天的旧备份。"
保存并退出 vi:
text
按 Esc
输入 :wq
按 Enter
为脚本添加执行权限:
bash
chmod 700 /root/backup_myproject_mysql.sh
检查权限:
bash
ls -l /root/backup_myproject_mysql.sh
正常应看到类似:
text
-rwx------ 1 root root ... /root/backup_myproject_mysql.sh
其中 x 表示脚本具有执行权限。
5. 手动测试备份
先手动运行一次脚本,确认配置正确:
bash
/root/backup_myproject_mysql.sh
正常情况下会输出类似:
text
[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 天的旧备份。
查看备份文件:
bash
ls -lh /root/MyProject_Backup/mysql/
正常会看到:
text
myproject_2026-06-26_200000.sql.gz
建议先确认手动备份成功,再配置自动定时任务。
6. 创建 systemd 服务
创建 service 文件:
bash
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
查看服务文件:
bash
cat /etc/systemd/system/myproject-mysql-backup.service
正常应显示:
ini
[Unit]
Description=MyProject MySQL Backup
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
ExecStart=/root/backup_myproject_mysql.sh
7. 创建每 5 天执行一次的 systemd 定时器
创建 timer 文件:
bash
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
参数说明:
ini
OnUnitActiveSec=5d
表示定时器启动后,每隔 5 天运行一次备份服务。
ini
Persistent=true
表示若服务器在计划执行时关机,系统恢复后会补做错过的备份任务。
8. 启用定时器并设置开机自动启动
执行:
bash
systemctl daemon-reload
systemctl enable --now myproject-mysql-backup.timer
systemctl restart myproject-mysql-backup.timer
检查定时器状态:
bash
systemctl status myproject-mysql-backup.timer
正常会显示类似:
text
Loaded: loaded (...; enabled; ...)
Active: active (waiting)
Trigger: ...
其中:
enabled:服务器重启后,定时器会自动启动;active (waiting):定时器正在等待下一次执行;Trigger:下一次自动备份的时间。
查看下次运行时间:
bash
systemctl list-timers --all | grep myproject-mysql-backup
检查是否已设置为开机启动:
bash
systemctl is-enabled myproject-mysql-backup.timer
正常应输出:
text
enabled
9. 测试 systemd 是否能正常调用备份脚本
手动触发一次 systemd 备份:
bash
systemctl start myproject-mysql-backup.service
查看日志:
bash
journalctl -u myproject-mysql-backup.service -n 30 --no-pager
正常会看到类似:
text
Starting MyProject MySQL Backup...
开始备份数据库:myproject
备份成功:/root/MyProject_Backup/mysql/myproject_2026-06-26_200000.sql.gz
已清理超过 30 天的旧备份。
Finished MyProject MySQL Backup.
这说明 systemd 可以正常调用备份脚本。
10. 常用备份管理命令
查看所有备份文件
bash
ls -lh /root/MyProject_Backup/mysql/
手动立即备份
bash
/root/backup_myproject_mysql.sh
或:
bash
systemctl start myproject-mysql-backup.service
查看自动备份状态
bash
systemctl status myproject-mysql-backup.timer
查看下一次备份时间
bash
systemctl list-timers --all | grep myproject-mysql-backup
查看备份日志
bash
journalctl -u myproject-mysql-backup.service -n 50 --no-pager
停止自动备份
bash
systemctl disable --now myproject-mysql-backup.timer
重新启用自动备份
bash
systemctl enable --now myproject-mysql-backup.timer
11. 如何恢复某一份数据库备份
假设需要恢复以下备份:
text
/root/MyProject_Backup/mysql/myproject_2026-06-26_200000.sql.gz
恢复会将当前数据库替换为历史备份版本。开始前请确认选中的备份文件正确。
11.1 先备份当前数据库
恢复前先生成一份当前数据库备份:
bash
/root/backup_myproject_mysql.sh
这样即使恢复了错误版本,也可以回退到恢复操作前的状态。
11.2 停止项目后端容器
先查看运行中的容器:
bash
docker ps
假设后端容器名称为:
text
myproject-backend
停止后端:
bash
docker stop myproject-backend
myproject-backend是示例名称,请替换为实际后端容器名称。MySQL 容器
myproject-mysql不需要停止。
11.3 删除当前数据库
bash
docker exec -it \
-e MYSQL_PWD="myproject123456" \
myproject-mysql \
mysql -uroot -e "DROP DATABASE IF EXISTS myproject;"
这条命令会删除当前的 myproject 数据库,因此必须确认前一步的备份已成功完成。
11.4 导入指定备份
bash
gunzip -c /root/MyProject_Backup/mysql/myproject_2026-06-26_200000.sql.gz \
| docker exec -i \
-e MYSQL_PWD="myproject123456" \
myproject-mysql \
mysql -uroot
导入过程可能没有额外输出;只要没有报错,一般表示恢复成功。
11.5 检查恢复结果
查看数据库:
bash
docker exec -it \
-e MYSQL_PWD="myproject123456" \
myproject-mysql \
mysql -uroot -e "SHOW DATABASES;"
查看恢复后的表:
bash
docker exec -it \
-e MYSQL_PWD="myproject123456" \
myproject-mysql \
mysql -uroot -e "USE myproject; SHOW TABLES;"
能够正常显示表名,通常说明恢复成功。
11.6 启动后端容器
bash
docker start myproject-backend
将 myproject-backend 替换成实际后端容器名称。
12. 完整恢复流程
bash
# 1. 备份恢复前的当前数据库
/root/backup_myproject_mysql.sh
# 2. 查看所有备份文件,确认需要恢复哪一份
ls -lh /root/MyProject_Backup/mysql/
# 3. 停止项目后端容器
docker stop myproject-backend
# 4. 删除当前数据库
docker exec -it \
-e MYSQL_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 \
| docker exec -i \
-e MYSQL_PWD="myproject123456" \
myproject-mysql \
mysql -uroot
# 6. 检查恢复后的数据表
docker exec -it \
-e MYSQL_PWD="myproject123456" \
myproject-mysql \
mysql -uroot -e "USE myproject; SHOW TABLES;"
# 7. 启动项目后端容器
docker start myproject-backend
13. 备份策略总结
| 项目 | 设置 |
|---|---|
| MySQL 容器 | myproject-mysql |
| 数据库名称 | myproject |
| 备份目录 | /root/MyProject_Backup/mysql |
| 备份脚本 | /root/backup_myproject_mysql.sh |
| 自动备份频率 | 每 5 天一次 |
| 备份保留时间 | 30 天 |
| 预计保留份数 | 约 6 份 |
| 备份格式 | .sql.gz |
| 自动删除旧备份 | 是 |
| 开机自动启用定时器 | 是 |
14. 进一步建议
本文中的备份文件仍然存放在同一台服务器,因此可以防止:
- 数据库误删;
- 错误更新或错误覆盖;
- 项目升级失败;
- 项目目录被误删;
- 数据库表被错误修改。
但它无法防止:
- 服务器硬盘损坏;
- 整台服务器不可用;
- 云服务器被删除;
- 勒索软件或严重系统故障。
对于重要数据库,建议定期将以下目录同步到另一块硬盘、NAS、对象存储或另一台服务器:
text
/root/MyProject_Backup/mysql/