Percona XtraBackup 實現全備&增量備份與恢復

神諭丶發表於2017-03-23
percona-xtrabackup主要是有兩個工具,其中一個是xtrabackup,一個是innobackupex,後者是前者封裝後的一個指令碼。
在針對MySQL的物理備份工具中,大概是最流行也是最強大的工具了,此外著名的物理備份工具還有官方的mysqlbackup。
xtrabackup只可備份事務表,不能用於備份非事務表,而innobackupex不僅可用於備份事務表,也可以備份非事務表如MyISAM
此文主要介紹innobakcupex的使用和原理。


本文所用到的版本:
xtrabackup 2.3.7 + MySQL 5.6.30


若MySQL版本為5.7.x,建議使用xtrabackup 2.4.x




〇 xtrabackup可以做的
    對InnoDB引擎的表做熱備
    增量備份
    流壓縮傳輸到另外的伺服器上
    線上移動表
    更簡單的建立從庫
    備份時不增加伺服器負載


 〇 原理
     備份及恢復大致涉及三個步驟:備份 -> prepare -> 恢復
     備份執行時,工具會記住當時的LSN號,並開啟xtrabackup_logfile,然後開始對datafile進行copy,即ibdata1及ibd檔案。
     複製需要一定的時間,在複製期間,如果檔案被修改,工具將監視redo log file並將每一次更變記錄下來,儲存在xtrabackup_logfile中。
     接下來處理非事務表如MyISAM的備份操作,innobackupex通過FLUSH TABLES WITH READ LOCK來阻塞DML。
     並在此時獲取binlog的position[和GTID](此處我理解為和mysqldump --single-transaction處理方式類似)
     在做完非事務表的copy之後,執行UNLOCK TABLES,完成備份,並停止記錄xtrabackup_logfile。
     接下來就是需要做prepare的過程,該過程類似InnoDB的crash-recovery。
     對redo log進行前滾(到資料檔案),並將沒提交的事務進行回滾操作(rollback),這樣便可以保證資料的一致性,所以對於事務表,整個過程是不會影響寫操作的。
 
     注:InnoDB、XtraDB、MyISAM是肯定支援的,其他的儲存引擎不確定,待測。
 
 〇 許可權需求
     作業系統:
         對datadir需要有rwx的許可權。
     MySQL:
         最小所需要的許可權有:
         RELOAD
         LOCK TABLES(如果加上--no-lock的話可以不要)
         REPLICATION CLIENT(為了獲得binary log的position)
         PROCESS(為了執行show engine innodb status,並且需要檢視所有執行的執行緒)
         其他可能需要用到的許可權:
         CREATE TABLESPACE(如果需要通過5.6+ 的TTS恢復/遷移單個表的話)
         SUPER(可能需要在複製環境裡啟動或者停止slave執行緒)
         CREATE\INSERT\SELECT(對PERCONA_SCHEMA.xtrabackup_history進行操作)
 
 〇 安裝
     安裝超簡單(只能在linux上用,不過但這就夠了)
     https://www.percona.com/downloads/XtraBackup/LATEST/
     戳進去選擇版本down下來很容易就可以用了。
     有RPM包、DEB包、原始碼包、二進位制包。
     個人推薦使用二進位制包,解壓,配置環境變數即可使用,在debian系或RHEL系通用,方便的一比。
     原始碼包的安裝,可以參考我這篇博文:
     http://blog.itpub.net/29773961/viewspace-1853405/
 
 〇 配置
     預設讀取my.cnf的選項,讀取優先順序與MySQL相同。
     比如在備份和恢復的時候無需指定datadir等,因為可以讀取[mysqld]組下的選項。
     同樣也可以讀取[client]的資訊,比如可以將socket,user,password載入到(雖然因為安全因素不建議使用,但是可以這麼做)。
     當然也可以通過innobackupex --defaults-file=xxxx/my.cnf 去指定將要讀取的配置檔案。
 
 〇 全備
 ① 備份:
     若加上--no-timestamp,則不會在所指定的目錄裡生成一個時間戳目錄,而是直接放在所指定的目錄裡,我一般是加的:
     innobackupex --user= --password= $basedir [--no-timestamp](當然--user/--password可以直接寫作 -u $username -p $password)
     
     在備份的資料夾中,有幾個檔案值得注意:
     xtrabackup_binlog_info記錄了binlog的position,若開啟了GTID,也會將GTID取出。
     在用於備份+binlog恢復或建立slave的場景裡十分有用。
     xtrabackup_checkpoints記錄了此次備份的型別和lsn號的起始值,是否壓縮等
     xtrabackup_info則記錄了備份工具的資訊,時間,備份物件(是針對全例項還是某庫表),是否是增量,binlog位置等
 
     # cat xtrabackup_binlog_info
     binlog.000001   2321    931d11a2-9a8b-11e6-829f-000c298e914c:1-8
  
     # cat xtrabackup_checkpoints
     backup_type = full-backuped
     from_lsn = 0
     to_lsn = 304247338
     last_lsn = 304247338
     compact = 0
      recover_binlog_info = 0
  
     # cat xtrabackup_info
     uuid = cfb49b5f-02e8-11e7-94b4-000c298e914c
     name = 
     tool_name = innobackupex
     tool_command = --password=... /data/dbbak
     tool_version = 2.3.7
     ibbackup_version = 2.3.7
     server_version = 5.6.30-log
     start_time = 2017-03-07 11:47:36
     end_time = 2017-03-07 11:47:39
     lock_time = 0
     binlog_pos = filename 'binlog.000001', position '2321', GTID of the last change '931d11a2-9a8b-11e6-829f-000c298e914c:1-8'
     innodb_from_lsn = 0
     innodb_to_lsn = 304247338
     partial = N
     incremental = N
     format = file
     compact = N
     compressed = N
     encrypted = N 
 

     還有一個backup-my.cnf檔案,則記錄了備份時可能涉及到的選項引數,比如系統表空間資訊,獨立undo表空間資訊,redo-log資訊等:
     # cat backup-my.cnf 
     # This MySQL options file was generated by innobackupex.
  
     # The MySQL server
     [mysqld]
     innodb_checksum_algorithm=innodb
     innodb_log_checksum_algorithm=innodb
     innodb_data_file_path=ibdata1:12M:autoextend
     innodb_log_files_in_group=2
     innodb_log_file_size=50331648
     innodb_fast_checksum=false
     innodb_page_size=16384
     innodb_log_block_size=512
     innodb_undo_directory=.
     innodb_undo_tablespaces=0 
 
 ② prepare:
     第二步就是prepare,前文也提到,這個過程類似innodb的crash recovery
     也可以理解為是“apply”的過程,這裡是全備prepare的命令,十分簡單
     innobackupex --apply-log $basedir
     在--apply-log的時候,可以指定--use-memory,增大其值加快速度,若不指定,預設值為100MB。
 
 ③ 恢復到datadir:
     恢復過程也十分簡單(全備和增備都是這一個恢復命令),只需要加上--copy-back引數即可
     innobackupex --copy-back $basedir
     這樣就可以將$basedir的東西恢復到datadir下了,datadir無需指定,將會讀取my.cnf獲得
     預設是需要datadir內為空的(或者沒有建立),如果要強制寫,則需要加引數: --force-non-empty-directories
 
 
 〇 增備
     
     增量備份比起全備要複雜一點,本文也想主要介紹如何做增量備份。
     用於有的場景,可能不需要每天對資料做全備。
     比如有的場景是,每週做一次全備,每天對做一次增量備份,可以節約磁碟空間也可以減少備份時間。
     增備的原理是通過對比LSN的資訊,來找到被更變的資料,當有了修改操作時,LSN號會改變,和上一次全備的差異LSN號做對比,則可將差異資料備份出來。
 
     整個過程還是分為三個步驟,備份 -> prepare -> 恢復
 
 ①增備方法與全備不一樣:
     innobackupex --user= --password= --incremental $new_dir --incremental-basedir=$basedir
 
    其中--incremental是本次增量備份存放目錄
     $new_dir是表示將增量備份出來的東西放在哪個目錄
     --incremental-basedir則表示,針對哪一次備份做增量備份
 
     備份的差異在目錄的xtrabackup_checkpoints中檢視:
     比如:
     $basedir中內容: 
     backup_type = full-prepared
     from_lsn = 0
     to_lsn = 304247338
     last_lsn = 304247338
     compact = 0
     recover_binlog_info = 0 
 
     $new_bkdir中內容:
     backup_type = incremental
     from_lsn = 304247338
     to_lsn = 304250267
     last_lsn = 304250267
     compact = 0
     recover_binlog_info = 0
  
     可以注意一下增備的from_lsn號
     大於這個LSN號的頁都是被變更過的,這些偏移量,也就是需要被增量備份出去的
 
 ②prepare:
     prepare過程:
     從第一個備份開始(也就是全量)做prepare,再將往後的增量備份依次新增到全量備份中。
     注意,此處多了一個引數即--redo-only,該引數是指將已提交的事務應用,未提交的事務回滾。
     此外,--incremental-dir也是在之前沒有用到過的,這個引數代表需要被合併進去的增量備份目錄。
     注意,此處多次的增量備份是指:針對上次的增量備份做的增量。
    
     也就是可以理解為:
         全備:500GB
         第一次增量備份:2GB
         第二次增量備份:1GB(針對第一次增量備份的增量資料)
         ……
         第n次
 
     按照備份順序做prepare,也就是prepare的順序為:
     第一次全備 -> 增量備份1 -> 增量備份2 -> ... -> 增量備份n
     第一次全備的prepare:innobackup --apply-log --redo-only $basedir
     第二次prepare:innobackup --apply-log --redo-only $basedir --incremental-dir=$new_dir_1(此處的$new_dir_1也就是第一次增量備份)
     ......
     第n次prepare:innobackup --apply-log $basedir --incremental-dir=$new_dir_n(此處的$new_dir_n也就是最近也就是最後一次的增量備份
     最後一次增量備份的prepare,不需要指定--redo-only
 
     最後將增量備份和全備進行合併,將未提交的事務回滾,這個操作和全量prepare無異:
     innobackup --apply-log $basedir
 
     看起來有點複雜,但沒關係,下面會有實驗和圖解。
 
 ③恢復到datadir:
     和全量無異,直接copyback就行了
     innobackupex --copy-back $basedir
 
  增量備份的prepare有點蛋疼,還是小結一下:
      ① prepare完備(加上--redo-only)
      ② prepare每一次增量備份到完備中,需要加上--redo-only,最後一次增量備份的prepare不需要加--redo-only
      ③ 對生成的最終完備做--apply-log
 
  



 〇 實驗
 
     接下來就是實驗……
     先建個備份用的使用者,給個許可權。
  1. mysql> CREATE USER xbackup@localhost IDENTIFIED BY 'back123';
  2. mysql> GRANT RELOAD, PROCESS, LOCK TABLES, REPLICATION CLIENT ON *.* TO xbackup@localhost;


   
 〇 完全備份&恢復
     在test.tb里加入測試資料
  1. mysql> CREATE TABLE test.tb(id int primary key, name varchar(16));
  2. Query OK, 0 rows affected (0.07 sec)
  3.  
  4. mysql> INSERT INTO test.tb VALUES(1,'zhou'),(2,'430'),(3,'YYF'),(4,'ChuaN'),(5,'Faith');
  5. Query OK, 5 rows affected (0.02 sec)
  6. Records: 5 Duplicates: 0 Warnings: 0

    建立備份存放目錄
     $ mkdir -p /data/backup/   
 
    指定備份存放位置,開始備份
     $ innobackupex -uxbackup -pbackup123 --no-timestamp /data/backup/backup
     此處的/data/backup/backup就是全備的目錄了。

    …………(省略刷屏輸出)
     xtrabackup: Transaction log of lsn (304289583) to (304290858) was copied.
     170321 16:06:11 completed OK!
 
     看到completed OK,表明就真的ok了。
 
     可以看一下這個目錄中的內容:
     一部分是MySQL下datadir的內容,如庫目錄,redolog,系統表空間。
     一部分是之前也有介紹過的,由備份工具生成的東西:
      backup-my.cnf
      ibdata1
      ib_logfile0    
      ib_logfile1
      mysql
      performance_schema
      test
      xtrabackup_binlog_info
      xtrabackup_binlog_pos_innodb
      xtrabackup_checkpoints
      xtrabackup_info
      xtrabackup_logfile
 
 
     進行prepare
 
    $ innobackupex --apply-log /data/backup/backup
 
     關閉mysqld
     $ mysqladmin -uroot -p shutdown
     Enter password: 
     $ ps -ef|grep mysql
     root       2991   2438  1 11:08 pts/0    00:00:00 grep mysql
 
     移除datadir:
     $ mv /data/mysql_data /data/mysql_data.bk
 
     恢復資料
     $ innobackupex --copy-back /data/backup/backup/
 
     修改新datadir的許可權
     $ chown mysql:mysql -R /data/mysql_data
 
     啟動資料庫
     $ mysqld &
     $ ps -ef|grep mysql
     root       2712   2438 86 16:35 pts/0    00:00:02 mysqld
     root       2714   2438  0 16:35 pts/0    00:00:00 grep mysql
 


    檢查test.tb中的內容 

  1. $ mysql -e "SELECT * FROM test.tb;"
  2. +----+-------+
  3. | id | name  |
  4. +----+-------+
  5. | 1  | zhou  |
  6. | 2  | 430   |
  7. | 3  | YYF   |
  8. | 4  | ChuaN |
  9. | 5  | Faith |
  10. +----+-------+

     至此,完全備份&恢復完成
 


 〇 增量備份&恢復
 
    先來一次全備:
     $ innobackupex -uxbackup -pbackup123 --no-timestamp /data/backup/all_backup
     修改測試表及資料:(加個欄位,改兩條資料)

  1. mysql> ALTER TABLE test.tb ADD COLUMN picked varchar(16);
  2. Query OK, 0 rows affected (0.06 sec)
  3. Records: 0 Duplicates: 0 Warnings: 0

  4. mysql> UPDATE test.tb SET picked='naga' WHERE id=1;
  5. Query OK, 1 row affected (0.04 sec)
  6. Rows matched: 1 Changed: 1 Warnings: 0
  7.  
  8. mysql> UPDATE test.tb SET picked='TA' WHERE id=2;
  9. Query OK, 1 row affected (0.00 sec)
  10. Rows matched: 1 Changed: 1 Warnings: 0

 
     執行第一次增量備份:
     $ innobackupex -uxbackup -pbackup123 --no-timestamp --incremental /data/backup/incremental-dir-1 --incremental-basedir=/data/backup/all_backup/ 
 
 
    可以再做一次增量備份:
     此時有兩種增量備份方法:
     第一種,總是針對basedir做增量,這個方式恢復起來就特別簡單了,只需要將最後一次的增量備份合併到全量備份裡,就可以恢復了。
     第二種,總是針對上一次的增量,做增量備份。這個方式的恢復,就要逐一合併了,也就是我上述所說看起來有點複雜的增備思路。
     反正我是喜歡第一種的,感覺也可以適應絕大多數場景。
 
      我拿word塗了兩張圖,幫助理解。
     第一種:
     總是將1月1日的全備作為basedir,所以FROM_LSN號總是5000。
     
 
     第二種:
     總是把上一次(最近一次)的備份作為basedir。
     
 


 
    此處介紹第二種:
    多次增量備份的方法依舊,只需要修改--incremental-basedir即可:
     
     繼續對test.tb做一些修改: 

  1. mysql> UPDATE test.tb SET picked='DS' WHERE id=3;
  2. Query OK, 1 row affected (0.04 sec)
  3. Rows matched: 1 Changed: 1 Warnings: 0
  4. mysql> SELECT * FROM test.tb;
  5. +----+-------+--------+
  6. | id | name  | picked |
  7. +----+-------+--------+
  8. | 1  | zhou  | naga   |
  9. | 2  | 430   | TA     |
  10. | 3  | YYF   | DS     |
  11. | 4  | ChuaN | NULL    |
  12. | 5  | Faith | NULL    |
  13. +----+-------+--------+
  14. 5 rows in set (0.01 sec)


     針對第一次增量備份/data/backup/incremental-dir-1,做第二次增量備份,將第二次的增量備份放到/data/backup/incremental-dir-2/
 
    $ innobackupex -uxbackup -pbackup123 --no-timestamp --incremental /data/backup/incremental-dir-2/ --incremental-basedir=/data/backup/incremental-dir-1
 

    prepare過程,這個也是增量備份裡最蛋疼的過程:
     
     因為總共做了三次備份,所以先做三次prepare:
     先對全備做prepare:
     $ innobackupex --apply-log --redo-only /data/backup/all_backup/
     然後接下來做第一次增量備份的prepare:
     $ innobackupex --apply-log --redo-only /data/backup/all_backup/ --incremental-dir=/data/backup/incremental-dir-1
     再對第二次的增量備份prepare,注意,第二次的增備是最後一次,所以不需要加上--redo-only引數:
     $ innobackupex --apply-log /data/backup/all_backup/ --incremental-dir=/data/backup/incremental-dir-2
     
     最後將兩次增量備份和全備做一次合併:
     $ innobackupex --apply-log /data/backup/all_backup/
 
     恢復過程,這個和全量恢復沒有區別:
 
    停掉mysqld
     $ mysqladmin -uroot -p shutdown
     $ ps -ef|grep mysql
     root       3533   3081  0 17:05 pts/1    00:00:00 grep mysql
     
     移除datadir
     $ mv /data/mysql_data /data/mysql_data.bk2
     
     恢復資料
     $ innobackupex --copy-back /data/backup/all_backup/
     
      修改新datadir的許可權
     $ chown mysql:mysql -R /data/mysql_data
 
      啟動
     $ mysqld &
 
    檢查一下,全備和兩次增備的內容都已經被恢復回來了,也就是最後一次資料的狀態: 
  1. $ mysql -uroot -p -e "SELECT * FROM test.tb;"
  2. +----+-------+--------+
  3. | id | name  | picked |
  4. +----+-------+--------+
  5. | 1  | zhou  | naga   |
  6. | 2  | 430   | TA     |
  7. | 3  | YYF   | DS     |
  8. | 4  | ChuaN | NULL    |
  9. | 5  | Faith | NULL    |
  10. +----+-------+--------+



      至此,增量備份&恢復完成。
 
 




 〇 總結一下xtrabackup備份及恢復全過程:
 
      1、備份操作,需要提供具有足夠許可權的MySQL使用者,並且mysqld啟動使用者需要對datadir有rwx的許可權。
      2、prepare,將未提交的事務回滾,將已提交的事務寫入資料檔案。
      3、停止mysqld服務
      4、mv data/ data_bak_.../
      5、copyback回去
      6、修改許可權新的datadir許可權
      7、啟動服務
 
     當然,上述所有的備份物件,都是針對整個MySQL例項。

 
 

 〇 參考資料:
 
 官方手冊:
 https://www.percona.com/doc/percona-xtrabackup/2.3/index.html
 雲棲社群@白及88使用者:
 https://yq.aliyun.com/articles/45746
 


 
 作者微信公眾號(持續更新)


 
 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29773961/viewspace-2135920/,如需轉載,請註明出處,否則將追究法律責任。

相關文章