MySQL中xtrabackup備份恢復全攻略(r12筆記第11天)
XtraBackup是Percona推出的一款備份工具,算是對於mysqldump的一個補充。對於大批量資料的匯入使用mysqldump會出現一定的瓶頸,這一點做過一些資料遷移專案的同學可能感同身受。
資料遷移中的資料量,小有小的好,大有大的招,見招拆招,找到適合的場景是最佳的。
如果現在去Percona官網下載,就會發現最新的版本已經是2.4.6了。下載可以選擇一個完整的打包,或者逐個的rpm根據需求來安裝也可以。完整的工具大概在60M左右。
而目前的MySQL版本大多都在5.5, 5.6, 對於5.7相對來說要新一些。中間會有一些時間的過渡,在多年前,可能相對來說用2.0版本一下的還比較多。
XtraBackup其實包含兩個工具,一個是xtrabackup,另外一個是innobackupex。我們暫且以一個較早的版本作為演示,然後使用新版本來對比下。
# xtrabackup --version
xtrabackup version 1.6.5 for Percona Server 5.1.59
innobackupex --version
InnoDB Backup Utility v1.5.1-xtrabackup; Copyright 2003, 2009 Innobase Oy
and Percona Inc 2009-2012. All Rights Reserved.
可以看到這兩個工具的版本還有一些差別,
xtrabackup主要是用於熱備份innodb,或者是 xtradb表中資料的工具,不能備份其他型別的表,也不能備份資料表結構;
innobackupex是將xtrabackup進行封裝的perl指令碼,可以備份和恢復MyISAM表以及資料表結構。
所以總體來看InnoDB的使用場景雖然最為普遍,但是還得考慮到MyISAM,兩者總體來說,使用innobackex的場景會多一些。
備份淺析
使用innobackupex備份,命令選項還不少,可以使用innobackupex --help來檢視明細的引數使用。
比如我需要做一個全備。可以採用如下類似的方式,在備份命令中加幾個輔助選項,備份使用socket連線,備份目錄在/home/databak/full/20170322下。
innobackupex --socket=/home/mysql/mysql.sock /home/databak/full/20170322 --no-timestamp --no-lock --throttle=100
備份後檢視對應的目錄,備份的資料情況如下,其中紅色的幾個檔案是備份中額外生成的。整體看來和源庫的目錄結構一樣。
# du -sh ./*
2.6G ./backend
4.0K ./backup-my.cnf
646M ./gm
1.0G ./ibdata1
99M ./mobile_activity
5.0G ./mobile_billing
1.1M ./mysql
2.0G ./oem_mon
212K ./performance_schema
112K ./test
4.0K ./xtrabackup_binary
4.0K ./xtrabackup_checkpoints
4.0K ./xtrabackup_logfile
對於上面生成的檔案,我們簡單看一下。
binary結尾的檔案是備份中用到的可執行檔案,這個可以對應幾個版本,比如xtrabackup_51,xtrabackup_55等
# more xtrabackup_binary
xtrabackup_55
logfile結尾的檔案的內容無法直接檢視,但是可以用strings來看。通過strings解析可以看到對應的二進位制日誌,當然事務的Xid也有的。
# strings xtrabackup_logfile
xtrabkup 170322 16:33:40
{ ';{
';{
MySQLXid
./mysql-bin.000009
393102654
08360000000039DB
下面的這個檔案就更特別了,這個是作為資料的備份恢復的關鍵,裡面有著備份恢復所有的檢查點LSN,從下面的資料來看,這是一個全備,因為from_lsn=0.
# cat xtrabackup_checkpoints
backup_type = full-backuped
from_lsn = 0
to_lsn = 30754980731
last_lsn = 30754980731
而在源庫的目錄結構下,我們稍作過濾,也會得到一個幾乎和這個工具備份出來一樣的目錄結構來。
# du -sh ./*|grep -v mysql-bin|grep -v innodb|grep -v log
2.6G ./backend
646M ./gm
1.0G ./ibdata1
4.5M ./ib_lru_dump
99M ./mobile_activity
5.0G ./mobile_billing
1.1M ./mysql
4.0K ./mysql.pid
0 ./mysql.sock
2.0G ./oem_mon
212K ./performance_schema
112K ./test
所以xtrabackup這樣一個工具就是一個熱備工具,有點類似有檔案級別的拷貝,但是不止於此,我們往下看。
全庫恢復模擬
資料恢復是DBA最重要的工作之一,多年之前,這個“之一“的字眼還要去掉。資料無法恢復,則備份無意義。
資料的恢復還是使用innobackupex這個工具,這是引數有些差別。
這裡的資料恢復分為兩個步驟,prepare和還原恢復,prepare的意義就在於,如果我們備份資料的時候,存在未提交的事務,但是資料卻存在於備份中,這樣就是一個數據不一致的狀態,在啟動資料庫的時候需要走一個前滾,然後是一個回滾的操作。這個體現主要就在於logfile和ibdata。是使用apply-log這個選項實現的。
我們使用如下的方式來做。
innobackupex --defaults-file=/home/databak/full/20170322/backup-my.cnf --user=root --apply-log /home/databak/full/20170322
這個過程其實就會隱式呼叫xtrabackup_55這個可執行檔案,呼叫的命令類似於:
xtrabackup_55 --defaults-file="/home/databak/full/20170322/backup-my.cnf"
預設會使用100M的記憶體,也可以使用選項--use-memory來調整,整個過程會重構redo日誌檔案和ibdata.
這個步驟完成之後就是最關鍵的地方了,還原恢復。這個過程是使用copy-back的選項實現的。
innobackupex --defaults-file=/home/databak/full/20170322/backup-my.cnf --user=root --copy-back /home/databak/full/20170322
整個過程就是大量的拷貝工作。
完成之後需要修改一下檔案的屬主,預設是root,然後啟動即可。
增量備份恢復 我們接下來看看增量備份和恢復,先來建立一些資料。我們在資料庫test下建立一個表test2.
> create table test2 (id int);
Query OK, 0 rows affected (0.01 sec)
> insert into test2 values(1),(2);
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
因為剛剛已經做了全備,我們繼續做一個增備。
使用的命令如下:
innobackupex --defaults-file=/etc/my.cnf --user=root --incremental-basedir=/home/databak/incre/20170322 --incremental /home/databak/incre/20170322
但是很不幸,執行失敗了。這個錯誤帶有典型的意義。
170322 18:05:34 innobackupex: Starting ibbackup with command: xtrabackup_55 --defaults-file="/etc/my.cnf" --backup --suspend-at-end --target-dir=/home/databak/incre/20170322/2017-03-22_18-05-32 --incremental-basedir='/home/databak/incre/20170322'
innobackupex: Waiting for ibbackup (pid=4079) to suspend
innobackupex: Suspend file '/home/databak/incre/20170322/2017-03-22_18-05-32/xtrabackup_suspended'
...
xtrabackup: Error: cannot open /home/databak/incre/20170322/xtrabackup_checkpoints
xtrabackup: error: failed to read metadata from /home/databak/incre/20170322/xtrabackup_checkpoints
innobackupex: Error: ibbackup child process has died at /usr/bin/innobackupex line 349.
原因就在於裡面的一個關鍵檔案 _checkpoints
使用增備得有一個參考點,從哪裡開始,即從哪個LSN開始,這個LSN在指定的引數--incremental-basedir=/home/databak/incre/20170322 下不存在,因為這個是一個新目錄,所以需要指向全庫備份的目錄。
然後修復後備份就沒問題了,英為有了這個參考點LSN,所以需要要說明的是這個備份其實有累計增量和差異增量了。
這個怎麼理解呢,比如週日做一個全備,週一做一個增備,週二做一個週日全備到週二的一個增備,這就是一個累計增量備份,而週三的時候做一個週二至週三資料變化的備份,就是一個差異增量備份。
下面的是一個累計增量備份。因為基準是上次的一個全備,備份後會自動生成一個目錄,比如 2017-03-22_18-07-38
innobackupex --defaults-file=/etc/my.cnf --user=root --incremental-basedir=/home/databak/full/20170322 --incremental /home/databak/incre/20170322
為了區別兩次增量,我繼續插入兩行資料。
> insert into test2 values (3),(4);
Query OK, 2 rows affected (0.00 sec)
這樣表test2就有4條資料了,每次插入2條。
下面的是一個差異增量備份。基於上一次的增備。
innobackupex --defaults-file=/etc/my.cnf --user=root --incremental-basedir=/home/databak/incre/20170322/2017-03-22_18-07-38 --incremental /home/databak/incre/20170322
整個恢復的過程是下面的形式,還是一個prepare的過程,首先是全備:
innobackupex --defaults-file=/etc/my.cnf --user=root --apply-log --redo-only /home/databak/full/20170322
然後是增備,注意這裡標紅的引數。
innobackupex --defaults-file=/etc/my.cnf --user=root --apply-log --redo-only /home/databak/full/20170322 --incremental-dir=/home/databak/incre/20170322/2017-03-22_18-07-38
這樣做其實是一個merge的過程,對於增備來說,會生成如下的幾個檔案,都是.delta, .meta之類的檔案。
[test]# ll
total 132
-rw-r--r-- 1 mysql mysql 61 Mar 22 17:58 db.opt
-rw-rw---- 1 mysql mysql 8556 Mar 22 18:03 test2.frm
-rw-r--r-- 1 root root 81920 Mar 22 18:08 test2.ibd.delta
-rw-r--r-- 1 root root 18 Mar 22 18:08 test2.ibd.meta
增備目錄下的checkpoint檔案就有意思了。有一個很清晰的LSN的增量描述。
[ 2017-03-22_18-07-38]# cat *checkpoints
backup_type = incremental
from_lsn = 30754980731
to_lsn = 30754984465
last_lsn = 30754984465
而prepare之後的全備裡面的checkpoint檔案其實已經發生了變化
# cat *checkpoints
backup_type = full-prepared
from_lsn = 0
to_lsn = 30754984465
last_lsn = 30754984465
這個時候我們使用如下的方式來還原恢復。
#innobackupex --defaults-file=/etc/my.cnf --user=root --copy-back /home/databak/incre/20170322/2017-03-22_18-07-38
這個時候表test2裡面的資料是幾條? 是2條。
這個過程我們相當於完成了一個全備+一個增備的資料恢復過程。
而我們在一個增備之後又插入了一些資料,這個怎麼繼續恢復呢,還是prepare的過程。這個路徑需要注意,還是merge到全備中。
innobackupex --defaults-file=/etc/my.cnf --user=root --apply-log --redo-only /home/databak/full/20170322 --incremental-dir=/home/databak/incre/20170322/2017-03-22_18-11-26
繼續還原恢復。
innobackupex --defaults-file=/etc/my.cnf --user=root --copy-back /home/databak/full/20170322
再次檢視資料,我們要恢復的4條資料都恢復回來了。
> select *from test2;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
| 4 |
+------+
4 rows in set (0.06 sec)
備份中的選項補充
innobackupex中的選項很多,有幾個還是比較有特色的,比如stream選項,--slave-info選項能夠方便搭建從庫,生成偏移量的資訊,比如並行--parallel等,還可以根據LSN來備份,選項是--incremental-lsn
對於stream選項,預設是打包,可以結合管道來實現壓縮,比如:innobackupex --defaults-file=/etc/my.cnf --user=root --stream=tar /home/databak/full/20170322_2 | gzip > /home/databak/full/20170322_2/20170322_2.tar.gz
備份中的常用場景
很多時候其實我不想備份整個庫,我只想備份一個表,那麼這個操作如何來實現呢。
innobackupex --defaults-file=/etc/my.cnf --user=root --include='test.test2' /home/databak/full/20170322_2
這裡有幾點需要注意,工具還是會逐個去掃描,只是那些不符合的會被忽略掉,也就意味著備份出來的情況和全備的目錄結構是一樣的,但是指定的表會備份出ibd,frm檔案。
[test]# ll
total 1036
-rw-r--r-- 1 mysql mysql 8556 Mar 22 18:34 test2.frm
-rw-r--r-- 1 root root 1048576 Mar 22 19:26 test2.ibd
[ test]# cd ../mysql
[ mysql]# ll
total 0
而且有一點值得吐槽一下的是,ibdata也會完整備份出來,如果這個檔案很大,那就相當不給力了。
不過也別對這種備份失去信心,有一個場景還是很實用的。那就是遷移表。
遷移表
還是剛剛的這個場景,如果表test2需要拷貝到另外一套環境中,我們可以使用Innobackupex來做物理備份,然後還原匯入,達到遷移的目的。
下面的命令會宣告指定目錄下的備份需要匯出物件。
innobackupex --apply-log --export /home/databak/full/20170322_2/2017-03-22_19-26-46
這個過程的直接產物就是生成了一個.exp檔案,在MySQL原生版本中是.cfg檔案
[ test]# ll
total 1052
-rw-r--r-- 1 root root 16384 Mar 22 19:29 test2.exp
-rw-r--r-- 1 mysql mysql 8556 Mar 22 18:34 test2.frm
-rw-r--r-- 1 root root 1048576 Mar 22 19:26 test2.ibd
對錶test2做資料資訊截斷。
> alter table test2 discard tablespace;
Query OK, 0 rows affected (0.07 sec)
然後就是物理拷貝,複製.exp檔案和.ibd檔案到指定目錄下,修改屬主許可權。
然後使用import的方式即可完成匯入。
> alter table test2 import tablespace;
Query OK, 0 rows affected (0.00 sec)
有另外一點值得說的是,這個.exp檔案是不是必須的,其實也不是。
我們只拷貝.ibd檔案也照樣可以。可能在新版本中會有一些警告提示,我們重新來做一下。
[test]> alter table test2 discard tablespace;
Query OK, 0 rows affected (0.03 sec)
同時刪除剛剛拷貝過來的.exp檔案。
然後拷貝ibd檔案到指定目錄,賦許可權
匯入表空間資訊。
[test]> alter table test2 import tablespace;
Query OK, 0 rows affected (0.00 sec)
檢視資料的情況,發現數據還是回來了。
[test]> select *from test2;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
| 4 |
+------+
4 rows in set (0.00 sec)
當然這個過程中還是有很多需要注意的地方。