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 实现自动化调度
  • 定期清理旧备份节省空间
相关推荐
航Hang*2 小时前
第3章:复习篇——第4节:创建、管理视图与索引---题库
网络·数据库·笔记·sql·学习·mysql·期末
霖霖总总3 小时前
[小技巧35]深入 InnoDB 的 LRU 机制:从原理到调优
数据库·mysql·性能优化
风行無痕3 小时前
MySQL 8.4 数据库修改字段长度的过程
数据库·mysql
符哥20084 小时前
基于mysql如何设置一个商城的数据库结构
数据库·mysql·oracle
chuxinweihui4 小时前
MySQL库数据类型
数据库·mysql
XerCis4 小时前
PostgreSQL与MySQL的超全对比(含迁移步骤)
数据库·mysql·postgresql
a***59265 小时前
MySQL数据可视化实战技巧
数据库·mysql·信息可视化
liux35285 小时前
MySQL执行计划与索引优化全面解析(三)
android·mysql·adb
warton885 小时前
proxysql配置mysql mgr代理,实现读写分离
linux·运维·数据库·mysql