mysql大數據量且多存儲引擎場景下的完整+增量自動異地備份的可靠方案

假想場景:數據庫Mysql5.1.X,已做“主-從”,在其中一臺從庫上備份,主庫不執行備份,從庫不停止,主從數據不一樣的時間差儘量短,在備份中有slave信息(主庫的二進制日誌名和位置),以便於在不停主庫的情況下,新建主-從,或者隨時可恢復到某個時間點。MyISAM和InnoDB都存在,備份要保證數據一致性。

基於以上場景,採用了innobackupex+bash shell,實現自動完整+增量備份,實現自動郵件通知和FTP上傳,以及本地和異地的備份滾動。

腳本中也有其它被註釋了的備份方式,以供參考。

下載包,這裏我下載了二制包

wget http://www.percona.com/redir/downloads/XtraBackup/LATEST/binary/Linux/x86_64/percona-xtrabackup-2.0.4-484.tar.gz

把命令拷貝到/usr/bin下即可

腳本的大致思路:以天爲單位,首次運行是完整備份,下次將增量備份,恢復到最近的數據需要恢復所有增量備份。如果完整備份失敗,那麼要刪除當天的備份目錄,再開始。因爲腳本是判斷lsn.txt文件是否存在來決定本次該完整備份還是增量備份。

 

在介紹腳本之前,首先要說說Innodb存儲引擎的熱備

Innodb 存儲引擎由於是事務性存儲引擎,有redo 日誌和相關的undo 信息,而且對數據的一致性和完整性的要求也比MyISAM 要嚴格很多,所以Innodb 的在線(熱)物理備份要比MyISAM 複雜很多,比如你要考慮到備份的數據一致性,對備份MYSQL的影響,緩存的污染,CPU、磁盤I/O和網絡的壓力,恢復的難度,時間和恢復的方式等等,一般很難簡單的通過幾個手工命令來完成,大都是通過專門的Innodb在線物理備份軟件來完成。

 

現在有比較好的軟件:

一個是開源的:

http://www.percona.com/software/percona-xtrabackup

innobackupex和xtrabackup

innobackupex是一個perl腳本集合,可以備份所有存儲引擎,並可以保證其數據一致性,它會調用xtrabackup

xtrabackup是一個C語言編寫的程序,只能備份innodb和xtrdb存儲引擎的表

 

一個是被MySQL官方合併到付費企業版的

http://www.innodb.com/products/hot-backup/

官方文檔:http://dev.mysql.com/doc/mysql-enterprise-backup/3.5/en/index.html

mysqlbackup和ibbackup

mysqlbackup是一個C語言編寫的程序,可以備份所有存儲引擎,並可以保證其數據一致性,是一個會調用ibbackup的命令

ibbackup只能備份innodb和存儲引擎的表

另外innobackup可能是之前www.innodb.com做的一個perl命令集合。

至少在mysqlbackup3.5軟件包中,innobackup和ibbackup命令被軟鏈接到mysqlbackup命令,innobackup和ibbackup命令在未來很可能被替代。

 

innobackupex安裝配置和使用

到官網下載相應版本的包,二進制包、RPM包、源碼包均可。經測試,發現通過源碼包安裝非常麻煩,因爲安裝過程容易出很多問題。建議直接下載二進制包使用,設置一個PATH或者拷貝到/usr/bin下即可使用了

權限配置說明:

RELOAD and LOCK TABLES (unless the --no-lock option is specified) in order to FLUSH TABLES WITH

READ LOCK prior to start copying the files and

REPLICATION CLIENT in order to obtain the binary log position,

CREATE TABLESPACE in order to import tables (see Importing and Exporting Individual Tables) and

SUPER in order to start/stop the slave threads in a replication environment.

 

mysql>

CREATE USER 'bakuser'@'localhost' IDENTIFIED BY 'pass';

REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'bakuser'@'localhost';

GRANT RELOAD, LOCK TABLES, REPLICATION CLIENT,SUPER ON *.* TO 'bakuser'@'pass';

FLUSH PRIVILEGES;

或:

GRANT RELOAD, LOCK TABLES, REPLICATION CLIENT,SUPER ON *.* TO 'bakuser'@'localhost' IDENTIFIED BY 'pass';

FLUSH PRIVILEGES;

注意,通過socket連接mysql實際上對root@localhost授權就夠了。

 

示例:

下面是單獨運行命令成功過的示例:

innobackupex --socket=/opt/mysql/var/mysql.sock --user=bakuser --password=pass --defaults-file=/opt/mysql/my.cnf --throttle=2 --slave-info --safe-slave-backup --use-memory=200MB /opt/data_bak/mysql_bak

在某次完整備份後第一次增量備份:

innobackupex --socket=/opt/mysql/var/mysql.sock --user=bakuser --password=pass --defaults-file=/opt/mysql/my.cnf --throttle=2 --slave-info --safe-slave-backup --use-memory=200MB --incremental --incremental-basedir=BASEDIR /opt/data_bak/mysql_bak

在最近完整備份後第二、三次增量備份:

或基於目錄

innobackupex --socket=/opt/mysql/var/mysql.sock --user=bakuser --password=pass --defaults-file=/opt/mysql/my.cnf --throttle=2 --slave-info --safe-slave-backup --use-memory=200MB --incremental --incremental-basedir=BASEDIR /opt/data_bak/mysql_bak

innobackupex --socket=/opt/mysql/var/mysql.sock --user=bakuser --password=pass --defaults-file=/opt/mysql/my.cnf --throttle=2 --slave-info --safe-slave-backup --use-memory=200MB --incremental --incremental-basedir=incrementaldir1 /opt/data_bak/mysql_bak

或基於日誌LSN

innobackupex --socket=/opt/mysql/var/mysql.sock --user=bakuser --password=pass --defaults-file=/opt/mysql/my.cnf --throttle=2 --slave-info --safe-slave-backup --use-memory=200MB --incremental --incremental-lsn=0:173917517 /opt/data_bak/mysql_bak

innobackupex --socket=/opt/mysql/var/mysql.sock --user=bakuser --password=pass --defaults-file=/opt/mysql/my.cnf --throttle=10 --slave-info --safe-slave-backup --use-memory=200MB --incremental --incremental-lsn=0:173918957 /opt/data_bak/mysql_bak

 

恢復:

先合併增量到完整備份

innobackupex --apply-log --redo-only BASE-DIR

innobackupex --apply-log --redo-only BASE-DIR --incremental-dir=INCREMENTAL-DIR-1

innobackupex --apply-log BASE-DIR --incremental-dir=INCREMENTAL-DIR-2   這是最後一個,也就是最新的增量

innobackupex --apply-log BASE-DIR

解後恢復到數據目錄

更多資料:http://www.percona.com/software/percona-xtrabackup

腳本內容:

把腳本修改爲適合自已的實際場景的,幾乎只需要修改變量設定部分,變量設置不正確,腳本運行會報錯,通過腳本的日誌和innobackupex.log可以查看原因,比如MYSQL的配置文件路徑(切記my.cnf中需要有datadir和innodb等相關設置),權限,本地備份目錄,FTP目錄等一定要再三檢查。


#!/bin/bash 
#History 
###################################################### 
#update       author 
#2012/10/15   zhaoyn   create 
#2012/10/22   zhaoyn   add ftp 
#2012/11/28   zhaoyn   add mysqlhotcopy 
#2012/12/05   zhaoyn   Improve function 
#2012/12/12   zhaoyn   Backup of stored procedures and events 
#2012/12/24   zhaoyn   Increase the log output 
#2013/01/09-11   zhaoyn   Various physical storage engine hot backup, InnoDB incremental backup command 
#Note: use hot backup script, may also need some other pre-configured. 
# wget http://www.percona.com/redir/downloads/XtraBackup/LATEST/binary/Linux/x86_64/percona-xtrabackup-2.0.4-484.tar.gz 
# http://dev.mysql.com/doc/mysql-enterprise-backup/3.5/en/index.html 
# 30 */2 * * * root /root/sh/backup_mysql.sh >> backup_mysql.log 2>&1 
###################################################### 
 
########## variable ####################################### 
### base config ### 
backupdir=/data_bak/mysql_bak/innobackupex 
#mysqlhost="127.0.0.1" 
#mysqlport="3306" 
mysqlsocket="/opt/mysql/var/mysql.sock" 
mysqldbname="dbname" 
mysqluser="root" 
mysqlpw="pass" 
 
### hotcopy ### 
hc_mysqluser="bakuser" 
hc_mysqlpw="pass" 
mysqlconfig="/opt/mysql/my.cnf" 
iops=8000    # This option specifies a number of I/O operations (pairs of read+write) per second. 
incr="yes"   # yes or no, to turn on or off the daily incremental backups 
 
### ftp ### 
ftpip='1.2.3.4' 
ftpport='21' 
ftpuser='backup' 
ftppw='pass' 
ftpbackupdir="innobackupex" 
ftpsw="yes"  # To turn on or off the FTP upload 
ftpdeldate=$(date -d "15 days ago" +%Y%m%d) 
 
### mail ### 
servername="dbserver" 
mailfromadd='dbserver<[email protected]>' 
mailtoadd='user1<[email protected]>' 
#mailccadd='user2<[email protected]>' 
 
### lang path time ### 
export LANG=C 
export LC_ALL=C 
export PATH=/opt/mysql/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 
datetime=$(date +%Y%m%d-%H%M) 
todaydate=$(date +%Y%m%d) 
deldate=$(date -d "7 days ago" +%Y%m%d) 
 
 
####### function ####################################### 
function mailto() { 
# mail 
/usr/sbin/sendmail -t <<EOF 
From: $mailfromadd 
To: $mailtoadd 
Cc: $mailccadd 
Subject: $servername Backup directory $backupdir does not exist. 
---------------------------------- 
$servername Backup directory $backupdir does not exist 
---------------------------------- 
EOF 
} 
 
function mailto2() { 
# mail 
/usr/sbin/sendmail -t <<EOF 
From: $mailfromadd 
To: $mailtoadd 
Cc: $mailccadd 
Subject: $servername complete the mysql-backup 
---------------------------------- 
$servername complete the mysql-backup, innobackupex $teststring 
---------------------------------- 
EOF 
} 
 
function mailto3() { 
# mail 
/usr/sbin/sendmail -t <<EOF 
From: $mailfromadd 
To: $mailtoadd 
Cc: $mailccadd 
Subject: $servername backup problems. 
---------------------------------- 
$servername backup problems. 
---------------------------------- 
EOF 
} 
 
echo "" 
echo "" 
echo "`date` Start." 
echo "##################################" 
####### delete old backups ####################################### 
echo "`date` Delete old backup." 
if [ -d "$backupdir" ] && [ "$deldate" -gt 20120901 ]; then 
   cd $backupdir 
#  find "$backupdir" -maxdepth 1 -name "*mysqldump*.gz" -mtime +"$deldays" -exec rm -f {} \; 
#  find "$backupdir" -maxdepth 1 -name "*mysqlhotcopy*" -mtime +"$deldays" -exec rm -f {} \; 
   find "$backupdir" -maxdepth 1 -type d -name "$deldate" | xargs rm -rf 
else 
   echo "Backup directory does not exist, or did not get to date." 
   mailto 
   exit 1 
fi 
 
echo "`date` Start Backup." 
####### mysqldump #################################### 
#mysqldump -S"$mysqlsocket" -u$mysqluser -p$mysqlpw --opt --routines --triggers --master-data=2 \ 
#--default-character-set=utf8 --lock-all-tables --quote-names --flush-logs --hex-blob --force \ 
#"$mysqldbname" > "$mysqldbname"_mysqldump_${datetime}.sql 
 
#echo "`date` Begin to compress." 
#tar czpf "$mysqldbname"_mysqldump_${datetime}.tar.gz "$mysqldbname"_mysqldump_${datetime}.sql 
#sync;sleep 2;sync;sleep 2 
#rm -f "$mysqldbname"_mysqldump_${datetime}.sql 
 
 
####### physical cold backup #################################### 
#/etc/rc.d/init.d/mysqld stop 
#sleep 2s ;sync;sync 
#echo "`date` Begin to compress." 
#tar -czvpf mysql_${datetime}.tar.gz /etc/my.cnf /opt/mysql/var 
#/etc/rc.d/init.d/mysqld start 
 
 
####### physical hot backup #################################### 
 
### 1. unix/linux, only myisam, mysqlhotcopy ### 
#mysqlhotcopy -S "$mysqlsocket" -u "$mysqluser" -p "$mysqlpw" "$mysqldbname" --flushlog ./ 
#sync;sleep 2;sync;sleep 2 
#mv "$mysqldbname" "$mysqldbname"_mysqlhotcopy_${datetime} 
 
#mysqldump -S"$mysqlsocket" -u$mysqluser -p$mysqlpw --opt --routines --default-character-set=utf8 \ 
#--quote-names --force --no-data "$mysqldbname" > "$mysqldbname"_mysqldump_structure-func_${datetime}.sql 
 
#echo "`date` Begin to compress." 
#tar czpf "$mysqldbname"_mysqlhotcopy_${datetime}.tar.gz \ 
#"$mysqldbname"_mysqlhotcopy_${datetime} "$mysqldbname"_mysqldump_structure-func_${datetime}.sql 
#sync;sleep 2;sync;sleep 2 
#rm -rf "$mysqldbname"_mysqlhotcopy_${datetime} "$mysqldbname"_mysqldump_structure-func_${datetime}.sql 
 
 
### 2. unix/linux, any storage engine, innobackupex, all databases ### 
test -e "$todaydate" || mkdir "$todaydate" 
cd "$todaydate" 
i=0 
if [ ! -f lsn.txt ]; then 
    echo "`date` Start full backup." 
    # fullbackup 
    innobackupex --socket="$mysqlsocket" --user="$mysqluser" --password="$mysqlpw" --defaults-file="$mysqlconfig" \ 
    --throttle="$iops" --slave-info --safe-slave-backup --use-memory=200MB --no-timestamp "$datetime"_fullbackup >> \ 
    innobackupex.log 2>&1 
 
    grep "last_lsn" "$datetime"_fullbackup/xtrabackup_checkpoints | awk -F[" "] '{print $3}' >> lsn.txt 
 
    mysqldump -S"$mysqlsocket" -u$mysqluser -p$mysqlpw --opt --routines --default-character-set=utf8 --quote-names \ 
    --force --no-data "$mysqldbname" > "$datetime"_fullbackup/"$mysqldbname"_mysqldump_structure-func_${datetime}.sql 
    sync;sleep 2;sync;sleep 2 
    tar czpf "$datetime"_"$mysqldbname"_fullbackup.tar.gz "$datetime"_fullbackup 
    rm -rf "$datetime"_fullbackup 
    i=1 
fi 
 
last_lsn=`tail -1 lsn.txt` 
teststring=`tail -1 innobackupex.log | awk -F': ' '{print $(NF-0)}'` 
 
if [ "$last_lsn" == "" ] && [ "$i" -eq 0 ] && [ "$incr" == "yes" ]; then 
    echo "`date` Did not get to last_lsn, cancel incremental backup, exit." 
    mailto3 
    exit 1 
elif [ "$teststring" != 'completed OK!' ] && [ "$i" -eq 0 ] && [ "$incr" == "yes" ]; then 
    echo "`date` Last backup was not successful, cancel incremental backup, exit." 
    mailto3 
    exit 1 
elif [ "$teststring" == 'completed OK!' ] && [ "$i" -eq 0 ] && [ "$incr" == "yes" ]; then 
    # incremental backup 
    innobackupex --socket="$mysqlsocket" --user="$mysqluser" --password="$mysqlpw" --defaults-file="$mysqlconfig" \ 
    --throttle="$iops" --slave-info --safe-slave-backup --use-memory=200MB --no-timestamp \ 
    --incremental --incremental-lsn="$last_lsn" "$datetime"_incremental >> innobackupex.log 2>&1 
 
    grep "last_lsn" "$datetime"_incremental/xtrabackup_checkpoints | awk -F[" "] '{print $3}' >> lsn.txt 
 
    mysqldump -S"$mysqlsocket" -u$mysqluser -p$mysqlpw --opt --routines --default-character-set=utf8 --quote-names \ 
    --force --no-data "$mysqldbname" > "$datetime"_incremental/"$mysqldbname"_mysqldump_structure-func_${datetime}.sql 
    sync;sleep 2;sync;sleep 2 
    tar czpf "$datetime"_"$mysqldbname"_incremental.tar.gz "$datetime"_incremental 
    rm -rf "$datetime"_incremental 
fi 
 
teststring=`tail -1 innobackupex.log | awk -F': ' '{print $(NF-0)}'` 
if [ "$teststring" != 'completed OK!' ]; then 
    echo "`date` The backup failed, exit." 
    mailto3 
    exit 1 
elif [ "$teststring" == 'completed OK!' ] && [ "$i" -eq 1 ]; then 
    echo "`date` The full backup was successful." 
    mailto2 
fi 
 
### 3. cross-platform, any storage engine, mysqlbackup, all databases ### 
#mysqlbackup --socket="$mysqlsocket" --user=$mysqluser --password="$mysqlpw" --default-character-set=utf8 \ 
#--compress-level=1 --sleep=30 --with-timestamp backup --backup-dir=./ 
 
 
####### FTP backup #################################### 
if [ "$ftpsw" == 'yes' ] && [ "$ftpdeldate" -gt 20120901 ]; then 
echo "`date` Start uploading to offsite." 
ftp -v -n -i <<END 
open $ftpip $ftpport 
user $ftpuser $ftppw 
bin 
cd $ftpbackupdir/$ftpdeldate 
mdelete *.gz 
cd /$ftpbackupdir 
rmdir $ftpdeldate 
mkdir $todaydate 
cd $todaydate 
mput ${datetime}*.gz 
bye 
END 
else 
echo "`date` Not open FTP, or did not get to date. done." 
fi 
 
echo "`date` All completed."



建議:腳本中只啓用了innobackupex備份,實際上,MYSQL備份最好邏輯備份和物理備份都同時進行,並且還定期做一份數據結構的備份,使各種備份的優勢互補。

附mysqldump的備份腳本:

#!/bin/bash 
#History 
###################################################### 
#update       author 
#2012/10/15   zhaoyn   create 
#2012/10/22   zhaoyn   add ftp 
#2012/11/28   zhaoyn   add mysqlhotcopy 
#2012/12/05   zhaoyn   Improve function 
#2012/12/12   zhaoyn   Backup of stored procedures and events 
#2012/12/24   zhaoyn   Increase the log output 
 
# 30 4 * * * root /root/sh/backup_mysqldump.sh >> backup_mysqldump.log 2>&1 
###################################################### 
 
########## variable ####################################### 
### base config ### 
backupdir=/opt/data_bak/mysql_bak/mysqldump 
#mysqlhost="127.0.0.1" 
#mysqlport="3307" 
mysqlsocket="/opt/mysql/var/mysql.sock" 
mysqlbinpath="/opt/mysql/bin" 
mysqldbname="dbname" 
mysqluser="root" 
mysqlpw="pass" 
 
### ftp ### 
ftpip='1.2.3.4' 
ftpport='10021' 
ftpuser='backup' 
ftppw='pass' 
ftpbackupdir="backup_db/mysqldump" 
ftpsw="yes"  # To turn on or off the FTP upload 
 
### mail ### 
servername="dbname" 
mailfromadd='dbname<[email protected]>' 
mailtoadd='user1<[email protected]>' 
#mailccadd='user2<[email protected]>' 
 
### lang path time ### 
export LANG=C 
export LC_ALL=C 
export PATH="$mysqlbinpath":/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 
datetime=$(date +%Y%m%d-%H%M) 
todaydate=$(date +%Y%m%d) 
ftpdeldate=$(date -d "35 days ago" +%Y%m%d) 
deldate=$(date -d "15 days ago" +%Y%m%d) 
deldays=15 
 
 
####### function ####################################### 
function mailto() { 
# mail 
/usr/sbin/sendmail -t <<EOF 
From: $mailfromadd 
To: $mailtoadd 
Cc: $mailccadd 
Subject: $servername mysqldump directory $backupdir does not exist. 
---------------------------------- 
$servername mysqldump directory $backupdir does not exist. 
---------------------------------- 
EOF 
} 
 
function mailto2() { 
# mail 
/usr/sbin/sendmail -t <<EOF 
From: $mailfromadd 
To: $mailtoadd 
Cc: $mailccadd 
Subject: $servername complete the mysqldump 
---------------------------------- 
$servername complete the mysqldump. 
---------------------------------- 
EOF 
} 
 
function mailto3() { 
# mail 
/usr/sbin/sendmail -t <<EOF 
From: $mailfromadd 
To: $mailtoadd 
Cc: $mailccadd 
Subject: $servername mysqldump problems. 
---------------------------------- 
$servername mysqldump problems. 
---------------------------------- 
EOF 
} 
 
echo "" 
echo "" 
echo "`date` Start." 
echo "##################################" 
####### delete old backups ####################################### 
echo "`date` Delete old backup." 
if [ -d "$backupdir" ] && [ "$deldate" -gt 20120901 ]; then 
   cd $backupdir 
   find "$backupdir" -maxdepth 1 -name "*mysqldump*.gz" -mtime +"$deldays" -exec rm -f {} \; 
#  find "$backupdir" -maxdepth 1 -type d -name "$deldate" | xargs rm -rf 
#  find "$backupdir" -maxdepth 1 -type f -name *"$deldate"*.gz | xargs rm -f 
else 
   echo "Backup directory does not exist, or did not get to date." 
   mailto 
   exit 1 
fi 
 
echo "`date` Start Backup." 
####### mysqldump #################################### 
mysqldump -S"$mysqlsocket" -u$mysqluser -p$mysqlpw --opt --routines --triggers --master-data=2 \ 
--default-character-set=utf8 --lock-all-tables --quote-names --flush-logs --hex-blob --force \ 
"$mysqldbname" | gzip -7 > "$mysqldbname"_mysqldump_${datetime}.sql.gz 
sync;sleep 2;sync;sleep 2 
 
mysqldump -S"$mysqlsocket" -u$mysqluser -p$mysqlpw --opt --routines --triggers \ 
--default-character-set=utf8 --quote-names --force --no-data \ 
"$mysqldbname" | gzip > "$mysqldbname"_mysqldump_structure_${datetime}.sql.gz 
sync;sleep 2;sync;sleep 2 
 
if [ -s "$mysqldbname"_mysqldump_${datetime}.sql.gz ] && [ -s "$mysqldbname"_mysqldump_structure_${datetime}.sql.gz ]; then 
    echo "`date` mysqldump completed." 
    mailto2 
else 
    echo "`date` The backup file does not exist, or the size is zero, exit" 
    mailto3 
    exit 1 
fi 
 
####### FTP backup #################################### 
if [ "$ftpsw" == 'yes' ] && [ "$ftpdeldate" -gt 20120901 ]; then 
echo "`date` Start uploading to offsite." 
ftp -v -n -i <<END 
open $ftpip $ftpport 
user $ftpuser $ftppw 
bin 
cd $ftpbackupdir 
mdelete *${ftpdeldate}*.gz 
mput *${datetime}*.gz 
bye 
END 
else 
echo "`date` Not open FTP, or did not get to date. done." 
fi 
 
echo "`date` All completed."


原文:

http://www.zhaoyanan.cn/mysql-innobackupex.html

http://www.zhaoyanan.cn/mysql-mysqldump-auto.html


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章