小易说IT 小易说IT

MySQL 每日备份 Shell 脚本

特性:单库备份、压缩、保留 N 天自动清理过期备份、日志记录、锁文件防止并发、支持发送告警;兼容 MySQL5.7 / 8.0,MariaDB。 依赖:mysqldumpgzip,建议安装。

1. 脚本 mysql_backup.sh

#!/bin/bash
set -euo pipefail

########################### 配置区,请修改这里 ###########################
# MySQL 连接信息
MYSQL_USER="root"
MYSQL_PASS="你的数据库密码"
MYSQL_HOST="127.0.0.1"
MYSQL_PORT="3306"
# 需要备份的数据库名,多个空格分隔:DB1 DB2
DB_NAME="testdb"
# 备份存放目录
BACKUP_DIR="/data/mysql_backup"
# 备份文件保留天数
RETENTION_DAYS=7
# 日志文件
LOG_FILE="/var/log/mysql_backup.log"
# 锁文件,防止脚本重复运行
LOCK_FILE="/tmp/mysql_backup.lock"
###########################################################################

# trap:无论正常退出还是异常退出,都自动删除锁文件
trap 'rm -f ${LOCK_FILE}; echo "[$(date +%Y-%m-%d\ %H:%M:%S)] 脚本退出,清理锁文件" >> ${LOG_FILE}' EXIT

# 判断锁文件是否存在
if [ -f "${LOCK_FILE}" ]; then
    echo "[$(date +%Y-%m-%d\ %H:%M:%S)] 警告:备份任务正在运行,本次任务退出" >> ${LOG_FILE}
    exit 1
fi
touch ${LOCK_FILE}

# 创建备份目录(不存在则新建)
mkdir -p ${BACKUP_DIR}

# 时间戳
TIME_STAMP=$(date +%Y%m%d_%H%M%S)

echo "==================== 开始备份 ${DB_NAME} ====================" >> ${LOG_FILE}
echo "[$(date +%Y-%m-%d\ %H:%M:%S)] 开始执行数据库备份" >> ${LOG_FILE}

# 循环备份每个库
for db in ${DB_NAME}; do
    BACKUP_FILE="${BACKUP_DIR}/${db}_${TIME_STAMP}.sql.gz"
    echo "[$(date +%Y-%m-%d\ %H:%M:%S)] 正在备份库:${db},文件:${BACKUP_FILE}" >> ${LOG_FILE}

    # mysqldump 备份 + gzip压缩
    mysqldump -h${MYSQL_HOST} -P${MYSQL_PORT} -u${MYSQL_USER} -p${MYSQL_PASS} \
        --single-transaction --quick --lock-tables=false ${db} | gzip > ${BACKUP_FILE}

    # 判断备份是否成功
    if [ $? -eq 0 ]; then
        echo "[$(date +%Y-%m-%d\ %H:%M:%S)] ${db} 备份成功" >> ${LOG_FILE}
    else
        echo "[$(date +%Y-%m-%d\ %H:%M:%S)] ❌ ${db} 备份失败!" >> ${LOG_FILE}
        exit 1
    fi
done

# 删除超过保留天数的旧备份文件
find ${BACKUP_DIR} -name "*.sql.gz" -type f -mtime +${RETENTION_DAYS} -delete
echo "[$(date +%Y-%m-%d\ %H:%M:%S)] 已清理${RETENTION_DAYS}天前的备份文件" >> ${LOG_FILE}

echo "[$(date +%Y-%m-%d\ %H:%M:%S)] ✅ 全部数据库备份任务完成" >> ${LOG_FILE}
echo "============================================================" >> ${LOG_FILE}
echo "" >> ${LOG_FILE}

exit 0

参数说明

  • --single-transaction:InnoDB 热备,不锁表(生产 InnoDB 必加

  • --lock-tables=false:关闭锁表,MyISAM 引擎不适合,MyISAM 建议去掉 --single-transaction

  • --quick:大表备份,减少内存占用

⚠️ 安全提示:脚本明文写密码有风险。更安全方案:在~/.my.cnf写入账号密码,mysqldump 不再带 -p 参数。 示例 .my.cnf:

[mysqldump]
user=root
password=你的数据库密码
host=127.0.0.1

然后脚本里 mysqldump 去掉 -u -p -h 参数,chmod 600 ~/.my.cnf

2. 赋予执行权限 & 手动测试

# 赋予执行权限
chmod +x mysql_backup.sh

# 手动运行测试
./mysql_backup.sh

# 调试模式,看每一步执行
bash -x mysql_backup.sh

# 查看日志
tail -f /var/log/mysql_backup.log

3. 配置 cron 定时任务(每天凌晨 2 点自动备份)

# 编辑当前用户定时任务
crontab -e

添加一行:

0 2 * * * /root/mysql_backup.sh >> /dev/null 2>&1

脚本内部已经自带日志输出到/var/log/mysql_backup.log,所以 cron 这里不再重定向日志。

查看定时任务:

crontab -l

4. 备份恢复测试(重要!备份一定要验证能否恢复)

# 解压备份文件
gzip -d testdb_20260917_020000.sql.gz

# 恢复到数据库
mysql -uroot -p testdb < testdb_20260917_020000.sql

5. 可选增强:失败发送告警(企业微信 / 钉钉机器人)

在备份失败的 else 块内增加 curl 调用,例子(钉钉 webhook):

curl -s "https://oapi.dingtalk.com/robot/send?access_token=xxx" \
 -H "Content-Type: application/json" \
 -d '{"msgtype":"text","text":{"content":"MySQL数据库备份失败,请尽快检查!"}}'

6. 常见坑

  1. cron 环境 PATH 问题:脚本里mysqldump写全路径,用which mysqldump获取路径替换

  2. 磁盘满:可以增加磁盘空间判断,备份前检查磁盘使用率

  3. 权限问题:备份目录、日志目录要确保脚本用户有写入权限

  4. MySQL8.0:注意密码认证插件,如果 root 不能用密码登录,需要调整账号认证方式


本文原创作者:易君召,详见:https://www.yijunzhao.cc/about,转载请注明出处。

原文链接 https://www.yijunzhao.cc/archives/mysql-daily-backup-shell-script

欢迎访问 https://www.yijunzhao.cc/

https://www.yijunzhao.cc/