Docker MySQL 自动备份与恢复教程(systemd 定时任务)

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/