本文提供一個實用的 MySQL 備份 Shell 腳本,實現「每 7 天全量備份,其餘天增量備份」的策略。全量備份使用 mysqldump,增量備份則利用 mysqlbinlog 解析二進制日誌。
#!/bin/bash
# MySQL 備份腳本:每 7 天全量,其餘增量
BACKUP_DIR="/mydata/data1/backup"
mkdir -p "$BACKUP_DIR"
cd "$BACKUP_DIR"
# 檢查 7 天內是否有全量備份檔
LATEST_FULL=$(find . -type f -mtime -7 -name "*all.sql" | head -1)
if [ -z "$LATEST_FULL" ]; then
echo "執行全量備份..."
BACKUP_FILE=$(date +%F-%H-%M-%S)
mysqldump -uroot --lock-all-tables --flush-logs --master-data=2 --all-databases \
> "$BACKUP_DIR/${BACKUP_FILE}-all.sql"
if [ $? -eq 0 ]; then
echo "全量備份完成:${BACKUP_FILE}-all.sql"
else
echo "全量備份失敗!"
exit 1
fi
else
echo "偵測到 7 天內有全量備份,執行增量備份..."
cd ..
# 找出倒數第二新的 binlog 檔案(最新的正在寫入,不可直接備份)
LATEST_NUM=0
PREV_NUM=0
PREV_FILE=""
for f in $(ls mysql-bin.* 2>/dev/null | grep -v index); do
NUM=$(echo "$f" | cut -d. -f2)
if [ "$NUM" -gt "$LATEST_NUM" ]; then
PREV_NUM=$LATEST_NUM
PREV_FILE=$LATEST_FILE
LATEST_NUM=$NUM
LATEST_FILE=$f
elif [ "$NUM" -gt "$PREV_NUM" ]; then
PREV_NUM=$NUM
PREV_FILE=$f
fi
done
if [ -n "$PREV_FILE" ]; then
BASE_NAME=$(echo "$PREV_FILE" | cut -d. -f1)
SEQ_NUM=$(echo "$PREV_FILE" | cut -d. -f2)
mysqlbinlog "$PREV_FILE" > "$BACKUP_DIR/${BASE_NAME}-${SEQ_NUM}.sql"
if [ $? -eq 0 ]; then
echo "增量備份完成:${BASE_NAME}-${SEQ_NUM}.sql"
else
echo "增量備份失敗!"
exit 1
fi
else
echo "找不到可用的 binlog 檔案進行增量備份"
exit 1
fi
fi
| 參數 | 用途 |
|---|---|
--lock-all-tables | 全域讀鎖,確保全量備份一致性 |
--flush-logs | 刷新二進制日誌,產生新 binlog 檔案 |
--master-data=2 | 在輸出中包含 CHANGE MASTER 陳述式(註解形式),記錄 binlog 位置 |
mysqlbinlog | 將二進制日誌轉為 SQL 陳述式,實現增量備份 |
/usr/local/bin/mysql_backup.sh 並賦予執行權限crontab 每天凌晨執行:0 2 * * * /usr/local/bin/mysql_backup.sh > /var/log/mysql_backup.log 2>&1--single-transaction 替代 --lock-all-tables 減少鎖爭用(需所有表為 InnoDB)gzip)與加密