Docker 备份MySQL数据库完整指南

目录

概述

本文档详细介绍MySQL数据库的备份及还原方法,包括Shell脚本、Java代码及批处理三种方式,特别针对Docker环境下的MySQL备份进行了详细说明,并提供了完整的定时任务配置。

备份与还原基础

核心命令

备份命令:

mysqldump -h127.0.0.1 -P3306 -utest -p123456 student > backup_file.sql

还原命令:

mysql -h127.0.0.1 -P3306 -utest -p123456 student < backup_file.sql

重要注意事项

  1. 权限要求: 使用备份命令需要开启相关数据库权限

  2. 文件存在性: 确保要还原的SQL文件存在

  3. 数据库存在性: 还原前需要保证目标数据库已存在

  4. 用户权限: 建议使用root用户或具备足够权限的用户

三种备份实现方法

方法一:Shell脚本备份

1. 基础Shell脚本(非Docker环境)

#!/bin/bash
dateBackup=$(date +%Y-%m-%d_%H:%M:%S)
dir="/root/backupDB/sql/${dateBackup}"

# 创建备份目录
if [ ! -d "${dir}" ]; then
    mkdir ${dir}
    echo "创建文件夹成功"
else
    echo "文件夹已经存在"
fi

# 需要备份的数据库名
dbNames=(mybatis mybatis-plus)
for dbName in ${dbNames[@]}; do
    echo "-----------------> 备份 ${dbName} 数据库 <-----------------" >> /root/backupDB/log/${dateBackup}.log
    # 备份MySQL,1>>为正常输出,2>>为错误或警告输出到日志文件
    mysqldump -utest -p123456 ${dbName} 1>> ${dir}/${dbName}.sql 2>> /root/backupDB/log/${dateBackup}.log
    # 压缩文件(解压:gzip -d mybatis.sql.gz)
    gzip ${dir}/${dbName}.sql
done

# 清理过期备份
echo -e "\n----------------> 删除过期文件 <---------------------------" >> /root/backupDB/log/${dateBackup}.log
dirCount=`ls -l /root/backupDB/sql/|grep "^d"|wc -l`
if [ ${dirCount} -gt 7 ]; then
    # 删除超过七天的SQL备份
    find /root/backupDB/sql -mtime +6 -name "*_*" -exec rm -rf {} \;
    # 删除超过七天的日志
    find /root/backupDB/log -mtime +6 -name "*.log" -exec rm -rf {} \;
    echo -e "删除过期文件成功" >> /root/backupDB/log/${dateBackup}.log
else
    echo "删除过期文件失败,文件数量小于7" >> /root/backupDB/log/${dateBackup}.log
fi

2. Docker容器MySQL备份脚本

#!/bin/bash
dateBackup=$(date +%Y-%m-%d_%H:%M:%S)
dir=/opt/docker/mysql/backup/${dateBackup}

# 在宿主机新建目录,通过挂载会自动添加到容器
if [ ! -d "${dir}" ]; then
    mkdir ${dir}
    echo "创建文件夹 ${dir} 成功" >> ${dir}/error.log
else
    echo "创建文件夹 ${dir} 失败,文件夹已存在" >> ${dir}/error.log
fi

# 需要备份的数据库名
dbNames=(student teacher car dog cat)
for dbName in ${dbNames[@]}; do
    echo "-----------------> 备份 ${dbName} 数据库 <-----------------" >> ${dir}/error.log
    # Docker容器内执行备份命令
    docker exec -i 642c89599d9b sh -c "mysqldump -ubackup -pAdmin@123 -h127.0.0.1 -P32773 ${dbName} 1>> /opt/backup/${dateBackup}/${dbName}.sql 2>> /opt/backup/${dateBackup}/error.log"
    # 在宿主机压缩备份文件
    gzip ${dir}/${dbName}.sql
done

# 清理过期备份文件
echo -e "\n----------------> 删除过期文件 <---------------------------" >> ${dir}/error.log
dirCount=`ls -l /opt/docker/mysql/backup/|grep "^d"|wc -l`
if [ ${dirCount} -gt 7 ]; then
    find /opt/docker/mysql/backup/ -mtime +6 -name "*_*" -exec rm -rf {} \;
    echo -e "删除过期文件成功" >> ${dir}/error.log
else 
    echo "删除过期文件失败,文件数量小于7" >> ${dir}/error.log
fi

3. Docker容器备份配置

启动容器时添加备份目录挂载:

docker run -d \
  --name mysql_container \
  -v /opt/docker/mysql/backup:/opt/backup \
  -e MYSQL_ROOT_PASSWORD=123456 \
  mysql:8.0

执行脚本权限设置:

# 方法1:添加执行权限
chmod 777 backup.sh
./backup.sh

# 方法2:直接使用sh执行
sh ./backup.sh

方法二:Java代码备份

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MySQLBackup {
    public static void main(String[] args) throws Exception {
        // 备份示例
        dbBackUp("127.0.0.1", "root", "123456", "3306", 
                "mybatis,mybatis-plus", "D:\\backup\\");
        
        // 还原示例(注释掉,按需使用)
        // reduction("127.0.0.1", "3306", "root", "123456", 
        //          "mybatis-plus", "2021-07-27_14-19-04/mybatis-plus.sql");
    }

    /**
     * 数据库备份方法
     * @param host 主机地址
     * @param userName 用户名
     * @param password 密码
     * @param port 端口
     * @param dbNames 数据库名(多个用逗号分隔)
     * @param backupPath 备份路径
     */
    public static void dbBackUp(String host, String userName, String password, 
                               String port, String dbNames, String backupPath) {
        String format = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(new Date());
        File backupDir = new File(backupPath + format);
        
        if (!backupDir.mkdir()) {
            System.out.println("创建备份目录失败");
            return;
        }
        
        String[] dbNameArray = dbNames.split(",");
        for (String dbName : dbNameArray) {
            String pathSql = backupPath + format + "\\" + dbName + ".sql";
            String cmd = "cmd /c mysqldump" + " -h" + host + " -P" + port + 
                        " -u" + userName + " -p" + password + " " + dbName + " > " + pathSql;
            
            System.out.println("执行命令:" + cmd);
            
            try {
                Process process = Runtime.getRuntime().exec(cmd);
                if (process.waitFor() == 0) {
                    System.out.println(dbName + " 数据库备份成功!");
                } else {
                    System.out.println(dbName + " 数据库备份失败!");
                }
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 数据库还原方法
     * @param host 主机地址
     * @param port 端口
     * @param userName 用户名
     * @param password 密码
     * @param databaseName 数据库名
     * @param fileName 备份文件路径
     */
    public static void reduction(String host, String port, String userName, 
                               String password, String databaseName, String fileName) throws Exception {
        File datafile = new File(fileName);
        if (!datafile.exists()) {
            System.out.println(fileName + " 文件不存在,请检查");
            return;
        }
        
        String cmd = "cmd /c mysql -h" + host + " -P" + port + " -u" + userName + 
                    " -p" + password + " " + databaseName + " < " + datafile;
        
        System.out.println("还原命令:" + cmd);
        
        Process exec = Runtime.getRuntime().exec(cmd);
        if (exec.waitFor() == 0) {
            System.out.println(databaseName + " 数据库还原成功,还原文件:" + datafile);
        } else {
            System.out.println("数据库还原失败");
        }
    }
}

方法三:批处理备份

Windows批处理脚本示例:

@echo off
set dateBackup=%date:~0,4%-%date:~5,2%-%date:~8,2%_%time:~0,2%-%time:~3,2%-%time:~6,2%
set backupDir=D:\backup\mysql\%dateBackup%

if not exist "%backupDir%" (
    mkdir "%backupDir%"
    echo 创建备份目录成功
) else (
    echo 备份目录已存在
)

rem 备份数据库列表
set dbList=mybatis mybatis-plus student

for %%d in (%dbList%) do (
    echo 正在备份数据库: %%d
    mysqldump -h127.0.0.1 -P3306 -uroot -p123456 %%d > "%backupDir%\%%d.sql"
    if %ERRORLEVEL%==0 (
        echo %%d 备份成功
    ) else (
        echo %%d 备份失败
    )
)

echo 备份完成
pause

Docker容器MySQL备份

方法1:直接执行容器内命令

# 备份单个数据库
docker exec -i mysql_container mysqldump -uroot -p123456 testdb > testdb_backup.sql

# 备份所有数据库
docker exec -i mysql_container mysqldump -uroot -p123456 --all-databases > all_databases_backup.sql

方法2:通过容器网络备份

# 使用临时容器进行备份
docker run --rm \
  --network container:mysql_container \
  -v $(pwd):/backup \
  mysql:8.0 \
  mysqldump -h127.0.0.1 -uroot -p123456 --all-databases > /backup/mysql_backup.sql

方法3:数据卷备份

# 备份数据卷
docker run --rm \
  --volumes-from mysql_container \
  -v $(pwd):/backup \
  ubuntu \
  tar czf /backup/mysql_volume_backup.tar.gz /var/lib/mysql/

定时任务设置

Crontab配置

# 编辑定时任务
crontab -e

# 每天凌晨1点执行备份脚本
0 1 * * * /root/backupDB/backup.sh

# 查看定时任务
crontab -l

# 查看定时任务日志
tail -f /var/log/cron

环境变量问题解决

在Shell脚本中添加环境变量:

#!/bin/bash
# 用户环境变量
source ~/.bashrc
# 系统环境变量
source /etc/profile

在Crontab中添加环境变量:

# 用户环境变量
0 1 * * * . ~/.bashrc;/root/backupDB/backup.sh

# 系统环境变量
0 1 * * * . /etc/profile;/root/backupDB/backup.sh

Cron服务管理

# 启动Cron服务
systemctl start crond

# 停止Cron服务
systemctl stop crond

# 重启Cron服务
systemctl restart crond

# 查看Cron服务状态
systemctl status crond

Crontab重要注意事项

  1. 执行延迟: 新创建的cron job不会马上执行,至少要过2分钟

  2. 输出处理: 每条JOB执行后系统会发送邮件,建议重定向输出:> /dev/null 2>&1

  3. 服务重启: 当crontab突然失效时,尝试重启crond服务

  4. 删除警告: 千万别乱运行crontab -r,会删除用户的所有crontab

  5. 特殊字符: 在crontab中%有特殊含义,使用时需要转义:\%

输出重定向详解

# 标准输出重定向到空设备
command > /dev/null

# 错误输出重定向到标准输出
command 2>&1

# 所有输出都重定向到空设备
command > /dev/null 2>&1

其中:

  • 1表示stdout标准输出

  • 2表示stderr标准错误

  • &表示等同于

  • /dev/null代表空设备文件

远程备份传输

SCP传输配置

使用SCP将Docker容器中的备份文件传输到远程服务器:

# 传输单个备份文件
scp /opt/docker/mysql/backup/2021-07-27_14-19-14/student.sql.gz \
    user@remote-server:/backup/mysql/

# 传输整个备份目录
scp -r /opt/docker/mysql/backup/2021-07-27_14-19-14/ \
    user@remote-server:/backup/mysql/

# 在备份脚本中集成SCP传输
for dbName in ${dbNames[@]}; do
    # 备份数据库
    docker exec -i mysql_container mysqldump -uroot -p123456 ${dbName} > ${dir}/${dbName}.sql
    gzip ${dir}/${dbName}.sql
    
    # 传输到远程服务器
    scp ${dir}/${dbName}.sql.gz user@remote-server:/backup/mysql/
    if [ $? -eq 0 ]; then
        echo "${dbName} 备份文件传输成功"
    else
        echo "${dbName} 备份文件传输失败"
    fi
done

免密传输配置

# 生成SSH密钥对
ssh-keygen -t rsa

# 将公钥复制到远程服务器
ssh-copy-id user@remote-server

# 测试免密登录
ssh user@remote-server

最佳实践

1. 备份策略设计

  • 频率设置: 根据数据重要性设定备份频率(每日/每周)

  • 保留策略: 设置合理的备份保留天数(如7天)

  • 多地备份: 将备份文件存储在不同服务器或云存储

  • 压缩存储: 使用gzip压缩备份文件节省存储空间

2. 安全考虑

# 使用环境变量存储密码
export MYSQL_PASSWORD="your_password"
mysqldump -uroot -p$MYSQL_PASSWORD testdb > backup.sql

# 设置备份文件权限
chmod 600 backup.sql

# 创建专用备份用户
CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'secure_password';
GRANT SELECT, LOCK TABLES, SHOW VIEW ON *.* TO 'backup_user'@'localhost';

3. 监控和日志

# 备份脚本中添加监控
BACKUP_SIZE=$(stat -c%s backup.sql)
if [ $BACKUP_SIZE -lt 1000000 ]; then
    echo "警告: 备份文件异常小" | mail -s "备份警告" admin@example.com
fi

# 记录备份耗时
START_TIME=$(date +%s)
# ... 备份操作 ...
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
echo "备份耗时: ${DURATION}秒" >> /var/log/mysql_backup.log

4. 性能优化

# 使用单事务保证一致性
mysqldump --single-transaction --routines --triggers testdb > backup.sql

# 并行备份多个数据库
for dbName in ${dbNames[@]}; do
    {
        mysqldump -uroot -p$MYSQL_PASSWORD ${dbName} > ${dbName}_backup.sql
        gzip ${dbName}_backup.sql
    } &
done
wait  # 等待所有后台任务完成

常见问题与解决方案

Q1: Docker容器备份时提示找不到mysqldump命令

解决方案:

# 确保在正确的容器中执行命令
docker exec -it mysql_container which mysqldump

# 如果容器中没有mysqldump,使用MySQL客户端容器
docker run --rm --link mysql_container:mysql mysql:8.0 \
  mysqldump -hmysql -uroot -p123456 testdb > backup.sql

Q2: 定时任务无法执行,提示命令找不到

解决方案:

# 在脚本开头添加环境变量
#!/bin/bash
source /etc/profile
source ~/.bashrc
export PATH=/usr/local/bin:/usr/bin:/bin

# 或使用完整路径
/usr/bin/docker exec -i mysql_container /usr/bin/mysqldump -uroot -p123456 testdb

Q3: 大数据库备份超时

解决方案:

# 增加超时设置
mysqldump --single-transaction --lock-tables=false --routines \
  --set-gtid-purged=OFF --default-character-set=utf8mb4 \
  -uroot -p123456 large_db > backup.sql

# 使用mydumper进行并行备份
docker run --rm --link mysql_container:mysql \
  -v $(pwd):/backup \
  mydumper_image \
  mydumper -h mysql -u root -p 123456 -c -B large_db -o /backup/

Q4: 备份文件权限问题

解决方案:

# 设置正确的文件权限
umask 077  # 设置默认权限为600
mysqldump -uroot -p123456 testdb > backup.sql

# 修改已存在文件的权限
chmod 600 backup.sql
chown mysql:mysql backup.sql

Q5: 跨版本恢复兼容性问题

解决方案:

# 备份时指定兼容模式
mysqldump --compatible=mysql323 --skip-opt --create-options \
  -uroot -p123456 testdb > backup.sql

# 还原前检查版本兼容性
mysql --version

总结

Docker环境下的MySQL备份需要综合考虑多个因素:

  1. 选择合适的备份方法: Shell脚本适合Linux环境,Java代码适合跨平台,批处理适合Windows

  2. 配置可靠的定时任务: 注意环境变量和权限问题

  3. 实施多重备份策略: 本地备份+远程传输+云存储

  4. 建立监控和告警机制: 及时发现备份异常

  5. 定期测试恢复流程: 确保备份文件的可用性

通过本文档提供的完整方案,可以建立起稳定可靠的Docker MySQL备份体系,确保数据安全。

Last updated

Was this helpful?