MySQL 数据备份流程化

背景

在 Linux 机器上用 Docker 部署了 MySQL 数据库,现在需要定期备份数据。备份策略是先在本地备份一份,然后传输到远程服务器。

环境信息:

  • MySQL 容器:mysql-db
  • 数据卷路径:/home/docker/work_root/volumes/dev-db_server_mysql_data/_data
  • 数据库:dev-db
  • Linux:centos

备份方案选择

常见的备份方式有两种:

逻辑备份(mysqldump):导出 SQL 语句,可跨版本恢复,适合中小型数据库,灵活性高。

物理备份:直接复制数据文件,速度快,适合大型数据库,但 MySQL 版本需一致。

这里选择 mysqldump 方式,因为更灵活,后续恢复也方便。

备份脚本编写

本地备份

首先实现本地备份功能:

bash 复制代码
# 创建备份目录
mkdir -p ~/mysql_backups

# 使用 mysqldump 导出数据库
docker exec mysql-db mysqldump -uroot -p'password' \
  --single-transaction \
  --routines \
  --triggers \
  --events \
  --all-databases > ~/mysql_backups/backup_$(date +%Y%m%d_%H%M%S).sql

# 压缩备份文件节省空间
gzip ~/mysql_backups/backup_*.sql

参数说明:

  • --single-transaction:保证数据一致性
  • --routines:包含存储过程和函数
  • --triggers:包含触发器
  • --events:包含事件
  • --all-databases:备份所有数据库

远程传输

备份完成后需要传输到远程服务器。使用 scp 命令,但遇到一个问题:scp 本身不支持密码参数。

解决方案是使用 sshpass 工具:

bash 复制代码
# 安装 sshpass
sudo yum install sshpass -y

# 传输文件
sshpass -p 'password' scp -o StrictHostKeyChecking=no \
  backup.sql.gz root@远程IP地址:/home/autotest/

这里踩了个坑:第一次传输时遇到 "Host key verification failed" 错误,返回值是 6。原因是 SSH 主机密钥验证失败,需要添加 -o StrictHostKeyChecking=no 参数跳过验证。

完整备份脚本

将上述功能整合成一个完整的脚本 /opt/scripts/mysql_backup.sh

bash 复制代码
#!/bin/bash

# 配置参数
CONTAINER_NAME="mysql-db"
MYSQL_ROOT_PASSWORD="password"
DATABASE_NAME="all_databases"
BACKUP_DIR="$HOME/mysql_backups"
REMOTE_USER="root"
REMOTE_HOST="IP地址"
REMOTE_PASSWORD="password"
REMOTE_PATH="/home/autotest/"
RETENTION_DAYS=7

mkdir -p $BACKUP_DIR

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/${DATABASE_NAME}_${TIMESTAMP}.sql"

echo "=========================================="
echo "MySQL 数据库备份脚本"
echo "时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "=========================================="

# 1. 执行数据库备份
echo ""
echo "[1/5] 开始备份数据库 $DATABASE_NAME ..."
docker exec $CONTAINER_NAME mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" \
  --single-transaction \
  --routines \
  --triggers \
  --events \
  --set-gtid-purged=OFF \
  --all-databases > $BACKUP_FILE

if [ $? -eq 0 ] && [ -s $BACKUP_FILE ]; then
    echo "✓ 数据库备份成功"
    ls -lh $BACKUP_FILE
else
    echo "✗ 数据库备份失败"
    exit 1
fi

# 2. 压缩备份文件
echo ""
echo "[2/5] 压缩备份文件..."
gzip $BACKUP_FILE
BACKUP_FILE="${BACKUP_FILE}.gz"

if [ -f "$BACKUP_FILE" ]; then
    echo "✓ 压缩完成"
    ls -lh $BACKUP_FILE
else
    echo "✗ 压缩失败"
    exit 1
fi

# 3. 测试远程连接
echo ""
echo "[3/5] 测试远程服务器连接..."
sshpass -p "$REMOTE_PASSWORD" ssh \
  -o StrictHostKeyChecking=no \
  -o UserKnownHostsFile=/dev/null \
  -o ConnectTimeout=10 \
  ${REMOTE_USER}@${REMOTE_HOST} "echo '连接成功'" 2>/dev/null

if [ $? -eq 0 ]; then
    echo "✓ 远程服务器连接正常"
else
    echo "✗ 无法连接到远程服务器"
    exit 1
fi

# 4. 传输到远程服务器
echo ""
echo "[4/5] 传输文件到远程服务器..."
sshpass -p "$REMOTE_PASSWORD" scp \
  -o StrictHostKeyChecking=no \
  -o UserKnownHostsFile=/dev/null \
  $BACKUP_FILE ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH} 2>/dev/null

if [ $? -eq 0 ]; then
    echo "✓ 文件传输成功"
    
    FILENAME=$(basename $BACKUP_FILE)
    REMOTE_CHECK=$(sshpass -p "$REMOTE_PASSWORD" ssh \
      -o StrictHostKeyChecking=no \
      -o UserKnownHostsFile=/dev/null \
      ${REMOTE_USER}@${REMOTE_HOST} \
      "ls -lh ${REMOTE_PATH}${FILENAME} 2>/dev/null")
    
    if [ $? -eq 0 ]; then
        echo "远程文件信息:"
        echo "$REMOTE_CHECK"
    fi
else
    echo "✗ 文件传输失败"
    exit 1
fi

# 5. 清理旧备份
echo ""
echo "[5/5] 清理旧备份文件..."
DELETED=$(find $BACKUP_DIR -name "${DATABASE_NAME}_*.sql.gz" -mtime +$RETENTION_DAYS -delete -print | wc -l)
echo "✓ 已删除 $DELETED 个超过 $RETENTION_DAYS 天的旧备份"

echo ""
echo "=========================================="
echo "✓ 备份任务完成!"
echo "本地备份: $BACKUP_FILE"
echo "远程备份: ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}$(basename $BACKUP_FILE)"
echo "=========================================="

测试脚本:

bash 复制代码
chmod +x /opt/scripts/mysql_backup.sh
/opt/scripts/mysql_backup.sh

配置定时任务

使用 systemd 实现定时备份,需要创建两个文件:service 文件和 timer 文件。

创建 service 文件

编辑 /etc/systemd/system/mysql-backup.service

ini 复制代码
[Unit]
Description=MySQL Database Backup Service
After=network.target docker.service
Requires=docker.service

[Service]
Type=oneshot
User=root
Group=root
WorkingDirectory=/opt/scripts
ExecStart=/opt/scripts/mysql_backup.sh
StandardOutput=journal
StandardError=journal
SyslogIdentifier=mysql-backup
TimeoutStartSec=1800
Restart=no

[Install]
WantedBy=multi-user.target

创建 timer 文件

编辑 /etc/systemd/system/mysql-backup.timer

ini 复制代码
[Unit]
Description=MySQL Database Backup Timer
Requires=mysql-backup.service

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=300
OnBootSec=1min

[Install]
WantedBy=timers.target

配置说明:

  • OnCalendar=*-*-* 02:00:00:每天凌晨 2 点执行
  • Persistent=true:如果错过执行时间,立即执行
  • RandomizedDelaySec=300:随机延迟 0-5 分钟
  • OnBootSec=1min:启动后 1 分钟执行一次

启用定时任务

bash 复制代码
systemctl daemon-reload
systemctl enable mysql-backup.timer
systemctl start mysql-backup.timer

# 查看定时器状态
systemctl status mysql-backup.timer
systemctl list-timers --all | grep mysql

查看日志

bash 复制代码
# 查看定时器日志
journalctl -u mysql-backup.timer -n 20

# 查看备份任务日志
journalctl -u mysql-backup.service -n 50

# 实时查看日志
journalctl -u mysql-backup.service -f

# 查看今天的日志
journalctl -u mysql-backup.service --since today

手动触发测试

bash 复制代码
# 立即执行一次备份
systemctl start mysql-backup.service

# 查看执行状态
systemctl status mysql-backup.service

非 Docker 环境适配

如果 MySQL 不是用 Docker 安装的,需要做以下调整:

调整 service 文件

将:

ini 复制代码
After=network.target docker.service
Requires=docker.service

改为:

ini 复制代码
After=network.target mysql.service
Requires=mysql.service

调整备份脚本

删除:

bash 复制代码
CONTAINER_NAME="mysql-db"

将:

bash 复制代码
docker exec $CONTAINER_NAME mysqldump

改为:

bash 复制代码
/opt/mysql/bin/mysqldump

同时建议删除 2>/dev/null,这样能看到完整的错误信息。

踩坑记录

systemd 执行失败

手动执行脚本正常,但 systemd 执行失败,报错:

复制代码
Failed at step EXEC spawning /opt/scripts/mysql_backup.sh: Exec format error

原因是脚本第一行缺少 shebang #!/bin/bash,systemd 不知道用什么解释器执行。

解决方法:确保脚本第一行是 #!/bin/bash

mysqldump 命令找不到

日志显示:

复制代码
/opt/scripts/mysql_backup.sh: line 27: mysqldump: command not found

但在命令行手动执行是正常的。原因是 systemd service 的 PATH 环境变量不包含 mysqldump 路径。

解决方法有三种:

  1. 使用绝对路径(推荐):将 mysqldump 改为 /opt/mysql/bin/mysqldump (通过 which mysqldumpwhereis mysqldump 查看路径)
  2. 在 service 文件中设置 PATH 环境变量
  3. 在脚本开头添加 export PATH=/opt/mysql/bin:$PATH

我选择了第一种方式,最简单直接。

数据恢复

如果需要恢复数据:

bash 复制代码
# 解压备份文件
gunzip backup_20260113_225511.sql.gz

# Docker 环境恢复
docker exec -i mysql-db mysql -uroot -p'password' < backup_20260113_225511.sql

# 非 Docker 环境恢复
mysql -uroot -p'password' < backup_20260113_225511.sql

总结

整个备份方案包含本地备份、远程传输、定时执行三个核心功能。使用 systemd 管理定时任务比 cron 更现代化,日志查看也更方便。

关键点:

  • mysqldump 备份灵活可靠
  • sshpass 解决密码传输问题
  • systemd 实现自动化调度
  • 定期清理旧备份节省空间
相关推荐
阮胜昌13 小时前
MySQL 9.7.0 LTS 现已发布:扩展了社区功能,并为企业级应用增加了动态数据脱敏功能
数据库·mysql
码域空间14 小时前
锁住了检查,锁不住延迟——MySQL 读写分离架构下双重检查模式失效实录
数据库·mysql·架构
数据库小学妹16 小时前
MySQL 8.4还是9.7 LTS?8.0 EOL后升级怎么选
数据库·mysql·dba·版本升级·数据库运维
m0_5474866616 小时前
《数据库原理及应用教程MySQL 8.0》全套PPT课件2026
数据库·mysql
程序员夏洛16 小时前
MySQL 中的事务隔离级别有哪些?
数据库·mysql
冰暮流星16 小时前
mysql之字符串函数
数据库·mysql
梦想不只是梦与想17 小时前
MySQL 不同操作系统的安装方式
数据库·mysql·mysql安装
码农颜17 小时前
6.4.1 监听连接
java·数据库·mysql
菀亓2 天前
ubuntu系统mysql安装
mysql
程序员贺加贝2 天前
别被 saveBatch 骗了:一次 MyBatis-Plus 批量插入性能排查
mysql