1. 程式人生 > 其它 >mysqldump和xtrabackup備份原理實現說明

mysqldump和xtrabackup備份原理實現說明

背景:

MySQL資料庫備份分為邏輯備份和物理備份兩大類,猶豫到底用那種備份方式的時候先了解下它們的差異:

邏輯備份的特點是:直接生成SQL語句,在恢復的時候執行備份的SQL語句實現資料庫資料的重現。

物理備份的特點是:拷貝相關資料檔案。

這二種備份差異  :邏輯備份其備份、還原慢,但備份檔案佔用的空間小;物理備份其備份還原快,備份檔案佔用空間大。

到底選擇那種備份方式,具體根據自己的實際情況,如需要的是熱備還是冷備?資料量大不大?磁碟空間夠不夠等因素決定。

邏輯備份工具主要有:mysqldumpmysqlpumpmydumper物理備份工具主要有:xtrabackup

現在使用最多的備份就是mysqldump、xtrabackup(都支援支援熱備),本文就mysqldump(mysqlpump)和xtrabackup的原理進行下大致的說明。

邏輯備份:mysqldump,第三方備份工具mydumper的備份原理看MySQL備份mydumper的原理

1)引數說明,具體的引數可以用mysqldump --help檢視。

在說明mysqldump之前先了解下它的相關引數,可以通過mysqldump --help進行檢視,也可以通過mysqldump、mysqlpump備份工具說明mysqldump的流程進行了解。這裡再重申一下幾個比較重要的引數。

①:--single-transaction

通過將匯出操作封裝在一個事務(Repeatable Read)內來使得匯出的資料是一個一致性快照。只有當表使用支援MVCC的儲存引擎(目前只有InnoDB)時才可以工作;其他引擎不能保證匯出是一致的。當匯出開啟了–single-transaction選項時,要確保匯出檔案有效(正確的表資料和二進位制日誌位置),就要保證沒有其他連線會執行如下語句:ALTER TABLE, DROP TABLE, RENAME TABLE, TRUNCATE TABLE,這會導致一致性快照失效。這個選項開啟後會自動關閉lock-tables。並且在mysql5.7.11之前,--default-parallelism大於1的時候和此參也互斥,必須使用--default-parallelism=0。5.7.11之後解決了--single-transaction和--default-parallelism的互斥問題。

②:--master-data

這個選項可以把binlog的位置和檔名新增到輸出中,如果等於1,將會列印成一個CHANGE MASTER命令;如果等於2,會加上註釋字首。並且這個選項會自動開啟–lock-all-tables,除非同時設定了–single-transaction(這種情況下,全域性讀鎖只會在開始dump的時候加上一小段時間,不要忘了閱讀–single-transaction的部分)。在任何情況下,所有日誌中的操作都會發生在匯出的準確時刻。這個選項會自動關閉–lock-tables。開啟該引數需要有reload許可權,並且伺服器開啟binlog。

③:--lock-all-tables

,-x

鎖定所有庫中所有的表。這是通過在整個dump的過程中持有全域性讀鎖來實現的。會自動關閉–single-transaction 和 –lock-tables。

④:--lock-tables-l

備份某個庫就鎖該庫的所有表,用READ LOCAL來鎖表。MyISAM允許併發寫入,因為鎖表只針對指定的資料庫,不能保證物理上的一致性,不同庫的表備份完成時會有不同的狀態。用–skip-lock-tables來關閉。

⑤:--flush-logs-F

在開始匯出前重新整理伺服器的日誌檔案。注意,如果你一次性匯出很多資料庫(使用--databases= 或--all-databases 選項),匯出每個庫時都會觸發日誌重新整理。例外是當使用了--lock-all-tables、--master-data或--single-transaction時:日誌只會被重新整理一次,那個時候所有表都會被鎖住。所以如果你希望你的匯出和日誌刷新發生在同一個確定的時刻,你需要使用--lock-all-tables、--master-data和--single-transaction配合 –flush-logs。

⑥:--opt

該引數預設開啟,表示快遞啟動--add-drop-table --add-locks --create-options --disable-keys --extended-insert --lock-tables --quick --set-charset選項,通過 --skip-opt 關閉。

2)執行說明

①:邏輯備份就是匯出SQL形式的檔案,其的具體實現步驟可以直接開啟genaral_log。

general_log_file        = /var/log/mysql/mysql.log
general_log             = 1

②:備份需要保證資料庫的一致性,即在某一時刻,整個資料庫的狀態是一致的,這樣可以通過備份進行恢復成為另一個從庫。資料庫目前使用最多的儲存引擎是InnoDB,也有可能部分是MyISAM,建議把MyISAM儲存引起改成InnoDB。現在重點說明關於InnoDB的備份。

MyISAM表的備份選項:上面提過因為mysqldump預設開啟--opt選項,而--opt裡包含--lock-tables的選項,這個選項不能保證在多個數據庫下資料備份的一致性,所以要麼--skip-opt,再把需要的選項新增進去,要麼就再使用--lock-all-tables的選項(開啟之後會關閉--lock-tables的選項),要是在從庫備份則只需要新增--master-data選項(開啟之後自動開啟--lock-all-tables選項)即可。

備份myisam表的命令:

mysqldump -uroot -p123 --default-character-set=utf8 --master-data=1 -R -E --triggers -B dba_test dba_test2 > /home/dxy/dba_test.sql

因為開啟了--lock-all-tables選項(--master-data),保證一致性讀和資料的一致性。在備份開始時就會執行FLUSH TABLES WITH READ LOCK命令,這個命令是server層面的鎖,這樣後面任何儲存引擎執行DML、DDL語句都會Waiting for global read lock狀態,這樣就保證了從備份點之後資料的一致性。

InnoDB表的備份選項:和上面介紹MyISAM表的備份選項一樣,在此基礎上增加了--single-transaction的選項,這個選項保證了通過將匯出操作封裝在一個事務(Repeatable Read)內來使得匯出的資料是一個一致性快照。只有當表使用支援MVCC的儲存引擎(目前只有InnoDB)時才可以工作,其他引擎不能保證匯出是一致的。這個選項開啟後會自動關閉--lock-tables選項,而--master-data選項自動開啟–lock-all-tables選項,在設定了--single-transaction這種情況下,全域性讀鎖只會在開始dump的時候加上一小段時間(5.7之前),5.7之後不需要加鎖了

一致性快照,即一致性讀取,那是如何保證一致性讀的呢?具體的說明可以看MySQL 一致性讀 深入研究這篇文章。大致的說明可以看下面的測試說明:

測試1:

sesseion A session B
mysql>settx_isolation='repeatable-read'; QueryOK,0rowsaffected(0.00sec) mysql>settx_isolation='repeatable-read'; QueryOK,0rowsaffected(0.00sec)
mysql>begin; QueryOK,0rowsaffected(0.01sec)
mysql>select*fromt1; Emptyset(0.00sec) mysql>insertintot1(c1,c2)values(1,1); QueryOK,1rowaffected(0.01sec)
mysql>select*fromt1; +----+------+ |c1|c2| +----+------+ |1|1| +----+------+ 1rowinset(0.00sec)

結論1:RR隔離級別下的一致性讀,不是以begin開始的時間點作為snapshot建立時間點,因為測試看出再begin一個事務的時候,表是沒有資料的,sessionB寫入資料之後,卻能看到資料。

測試2:

session A session B
mysql>settx_isolation='repeatable-read'; mysql>settx_isolation='repeatable-read';
mysql>select*fromt1; Emptyset(0.00sec)
mysql>begin; mysql> select * from t;
mysql>insertintot1(c1,c2)values(1,1); QueryOK,1rowaffected(0.01sec)
mysql>select*fromt1; Emptyset(0.00sec)

結論2:RR隔離級別下的一致性讀,是以第一條select語句的執行點作為snapshot建立的時間點的,即使是不同表的select語句。這裡因為session A在insert之前對 t 表執行了select,所以建立了snapshot,所以後面的select * from t1 不能讀取到insert的插入的值(snapshot的時候t1表沒有資料)。

測試3:

session A session B
mysql>settx_isolation='repeatable-read'; mysql>settx_isolation='repeatable-read'; mysql>select*fromt1; Emptyset(0.00sec)
mysql>begin;
mysql>select*fromt1; Emptyset(0.00sec) mysql>select*fromt1; Emptyset(0.00sec)
mysql>insertintot1(c1,c2)values(1,1);
mysql>select*fromt1; Emptyset(0.01sec)

結論3:session A 的第一條語句,發生在session B的 insert語句提交之前,所以session A中的第二條select還是不能讀取到資料。因為RR中的一致性讀是以事務中第一個select語句執行的時間點作為snapshot建立的時間點的。而此時,session B的insert語句還沒有執行,所以讀取不到資料。

測試4:

session A session B
mysql>settx_isolation='repeatable-read'; mysql>settx_isolation='repeatable-read'; mysql>select*fromt1; Emptyset(0.00sec)
mysql>select*fromt1; Emptyset(0.00sec)
mysql>insertintot1(c1,c2)values(1,1),(2,2); mysql>select*fromt1; +----+------+ |c1|c2| +----+------+ |1|1| |2|2| +----+------+ 2rowsinset(0.01sec)
mysql>select*fromt1; Emptyset(0.00sec)
mysql>updatet1setc2=100wherec1=1; QueryOK,1rowaffected(0.00sec) Rowsmatched:1Changed:1Warnings:0 mysql>select*fromt1; +----+------+ |c1|c2| +----+------+ |1|100| +----+------+ 1rowinset(0.00sec)

結論4:本事務中進行修改的資料,即使沒有提交,在本事務中的後面也可以讀取到。update 語句因為進行的是“當前讀”,所以它可以修改成功。

通過上面的幾個測試得出的結論:對同一個表或者不同表進行的第一次select語句建立了該事務中一致性讀的snapshot,在snapshot建立之後提交的資料,一致性讀就讀不到,之前提交的資料就可以讀到。事務一致性讀的起始點其實是以執行的第一條語句為起始點的,而不是以begin作為事務的起始點的。

一般begin/start transaction是開始一個事務的標誌,但不是事務開始的時間點,也就是說執行了start transaction之後的第一個語句(任何語句),事務才真正的開始。但是如果要達到將 start transaction作為事務開始的時間點,那麼必須使用:

START TRANSACTION WITH consistent snapshot    ###mysqldump中的快照就是用這個實現的

這樣開啟事務效果等價於: start transaction 之後,馬上執行一條 select 語句(此時會建立一致性讀的snapshot)。

測試5:

session A session B
mysql>settx_isolation='repeatable-read'; mysql>settx_isolation='repeatable-read';
mysql>select*fromt1; Emptyset(0.01sec)
mysql>starttransaction;
mysql>insertintot1(c1,c2)values(1,1);
mysql>select*fromt1; +----+------+ |c1|c2| +----+------+ |1|1| +----+------+ 1rowinset(0.00sec)
測試6:
mysql>settx_isolation='repeatable-read'; mysql>settx_isolation='repeatable-read';
mysql>select*fromt1; Emptyset(0.01sec)
mysql>starttransaction with consistent snapshot;
mysql>insertintot1(c1,c2)values(1,1);
mysql>select*fromt1; Emptyset(0.00sec)
上面測試說明了 start transaction 和 start tansaction with consistent snapshot的區別。測試5說明,start transaction執行之後,一致性讀並沒有開始,所以可以讀到session B插入的值。測試6說明,start transaction with consistent snapshot已經開始了一致性快照,所以insert語句發生在事務開始之後,所以讀不到insert的資料。

所以事務一致性快照開始時間點,分為兩種情況

1)START TRANSACTION時,第一條語句的執行時間點,就是事務開始的時間點,第一條select語句建立一致性讀的snapshot;

2)START TRANSACTION  WITH consistent snapshot時,則是立即建立本事務的一致性讀snapshot,當然也開始事務了;

到此,大致說明了引數single-transaction的意義,其實就是通過start transaction with consistent snapshot實現一致性快照讀:通過將匯出操作封裝在一個事務內來使得匯出的資料是一個一致性快照。只有當表使用支援MVCC的儲存引擎(目前只有InnoDB)時才可以工作,其他引擎(MyISAM)不能保證匯出是一致的。如備份開啟之後,通過一致性快照記錄了mysql的binlog和position,此時往innodb表裡寫入資料,因為有一致性快照,備份讀不到最新的記錄,但是該操作會在之前記錄binlog之後位置裡記錄,當還原的時候,直接應用之後的binlog記錄即可。而myisam不支援事務,沒有快照,備份直接取最新資料,而寫入操作會持續記錄到binlog裡,所以還原的時候會導致一致性被破環。當還原到一個新從並開啟同步的change(備份裡面記錄的點)之後,myisam表會出現主鍵衝突,而innodb表不會。

備份InnoDB表的命令:

mysqldump -uroot -p123 --default-character-set=utf8 --single-transaction --master-data=1 -R -E --triggers -B dba_test dba_test2 > /home/dxy/dba_test.sql

上面講了這麼多,現在通過general_log看看mysqlbinlog備份步驟

2016-08-21T00:08:11.755486+08:00   15 Connect   root@localhost on  using Socket                         ##備份的連線方式
2016-08-21T00:08:11.793153+08:00   15 Query     /*!40100 SET @@SQL_MODE='' */                           ##備份的SQL_MODE
2016-08-21T00:08:11.815880+08:00   15 Query     /*!40103 SET TIME_ZONE='+00:00' */                      ##備份的時區,--tz-utc,用--skip-tz-utc關閉
##2016-08-21T00:08:11.815880+08:00   15 Query     FLUSH /*!40101 LOCAL */ TABLES                        ##重新整理表,--master-data的作用
##2016-08-21T00:08:11.815880+08:00   15 Query     FLUSH TABLES WITH READ LOCK                           ##加全域性讀鎖,--lock-all-tables的作用。
2016-08-21T00:08:11.815981+08:00   15 Query     SHOW STATUS LIKE 'binlog_snapshot_%'                    ##binlog的檔名和偏移量,--master-data的作用,或用SHOW MASTER STATUS表示
2016-08-21T00:08:11.822342+08:00   15 Query     SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ##--single-transaction的作用,設定成RR級別
2016-08-21T00:08:11.822457+08:00   15 Query     START TRANSACTION /*!40100 WITH CONSISTENT SNAPSHOT */  ##--single-transaction的作用,設定成一致性快照讀
2016-08-21T00:08:11.822675+08:00   15 Query     SHOW VARIABLES LIKE 'gtid\_mode'
2016-08-21T00:08:11.868728+08:00   15 Query     SHOW STATUS LIKE 'binlog_snapshot_%'                    ##binlog的檔名和偏移量,--master-data的作用,或用SHOW MASTER STATUS表示
2016-08-21T00:08:11.868940+08:00   15 Query     UNLOCK TABLES                                           ##解鎖表,印證了全域性讀鎖只會在開始dump的時候加上一小段時間

#上面黃色背景表示備份前的一些準備工作:一致性快照、鎖、二進位制日誌等資訊。

2016-08-21T00:08:11.900984+08:00 15 Query SELECT LOGFILE_GROUP_NAME, FILE_NAME, TOTAL_EXTENTS, INITIAL_SIZE, ENGINE, EXTRA FROM INFORMATION_SCHEMA.FILES WHERE FILE_TYPE = 'UNDO LOG' AND FILE_NAME IS NOT NULL AND LOGFILE_GROUP_NAME IS NOT NULL AND LOGFILE_GROUP_NAME IN (SELECT DISTINCT LOGFILE_GROUP_NAME FROM INFORMATION_SCHEMA.FILES WHERE FILE_TYPE = 'DATAFILE' AND TABLESPACE_NAME IN (SELECT DISTINCT TABLESPACE_NAME FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_SCHEMA IN ('dba_test','dba_test2'))) GROUP BY LOGFILE_GROUP_NAME, FILE_NAME, ENGINE, TOTAL_EXTENTS, INITIAL_SIZE, EXTRA ORDER BY LOGFILE_GROUP_NAME 2016-08-21T00:08:12.000013+08:00 15 Query SELECT DISTINCT TABLESPACE_NAME, FILE_NAME, LOGFILE_GROUP_NAME, EXTENT_SIZE, INITIAL_SIZE, ENGINE FROM INFORMATION_SCHEMA.FILES WHERE FILE_TYPE = 'DATAFILE' AND TABLESPACE_NAME IN (SELECT DISTINCT TABLESPACE_NAME FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_SCHEMA IN ('dba_test','dba_test2')) ORDER BY TABLESPACE_NAME, LOGFILE_GROUP_NAME 2016-08-21T00:08:12.000954+08:00 15 Query SHOW VARIABLES LIKE 'ndbinfo\_version' 2016-08-21T00:08:12.001309+08:00 15 Init DB dba_test 2016-08-21T00:08:12.001387+08:00 15 Query SHOW CREATE DATABASE IF NOT EXISTS `dba_test` 2016-08-21T00:08:12.001505+08:00 15 Query SAVEPOINT sp ##--single-transaction的作用 2016-08-21T00:08:12.001564+08:00 15 Query show tables 2016-08-21T00:08:12.001645+08:00 15 Query show table status like 'abc' 2016-08-21T00:08:12.001747+08:00 15 Query SET SQL_QUOTE_SHOW_CREATE=1 2016-08-21T00:08:12.001772+08:00 15 Query SET SESSION character_set_results = 'binary' 2016-08-21T00:08:12.001799+08:00 15 Query show create table `abc` 2016-08-21T00:08:12.001835+08:00 15 Query SET SESSION character_set_results = 'utf8' 2016-08-21T00:08:12.001864+08:00 15 Query show fields from `abc` 2016-08-21T00:08:12.002013+08:00 15 Query show fields from `abc` 2016-08-21T00:08:12.002150+08:00 15 Query SELECT /*!40001 SQL_NO_CACHE */ * FROM `abc` ##表示備份表的語句 2016-08-21T00:08:12.021228+08:00 15 Query SET SESSION character_set_results = 'binary'

#上面藍色背景表示備份一張表的過程

2016-08-21T00:08:12.021499+08:00 15 Query use `dba_test` 2016-08-21T00:08:12.021549+08:00 15 Query select @@collation_database 2016-08-21T00:08:12.021616+08:00 15 Query SHOW TRIGGERS LIKE 'abc' ##備份觸發器,--triggers 2016-08-21T00:08:12.039445+08:00 15 Query SET SESSION character_set_results = 'utf8' 2016-08-21T00:08:12.039561+08:00 15 Query ROLLBACK TO SAVEPOINT sp ... ... 2016-08-21T00:23:35.131333+08:00 16 Query show events ##備份事件,-E 2016-08-21T00:23:35.161440+08:00 16 Query use `dba_test` 2016-08-21T00:23:35.161513+08:00 16 Query select @@collation_database 2016-08-21T00:23:35.161582+08:00 16 Query SET SESSION character_set_results = 'binary' 2016-08-21T00:23:35.161795+08:00 16 Query SHOW FUNCTION STATUS WHERE Db = 'dba_test' ##備份函式,-R 2016-08-21T00:23:35.190912+08:00 16 Query SHOW PROCEDURE STATUS WHERE Db = 'dba_test' ##備份儲存過程,-R 2016-08-21T00:23:35.191683+08:00 16 Query SET SESSION character_set_results = 'utf8'
2016-08-21T00:23:35.191683+08:00 16 Quit ##備份完成退出

3)實現過程

通過2)裡的general_log的這些步驟,可以看到mysqldump的大致實現過程是:連線 -> 初始化資訊 -> 重新整理表(鎖表)-> 記錄偏移量 -> 開啟事務(一致性快照)-> 記錄偏移量 -> 解鎖表,因為開啟了一致性讀,可以得到innodb的一致性,又因為解鎖表了,MyISAM表一致性得不到保證,所以儘量別使用MyISAM表。

物理備份:xtrabackup

關於xtrabackup的備份原理可以看Percona XtraBackup 備份原理說明以及xtrabackup 使用說明(續)。這2篇文章已經講的很詳細了,本文就不再進行說明。

總結:

根據自己的實際情況,對備份原理、特性的認識,再選擇到底是使用物理備份和邏輯備份。特別需要注意的是在用mysqldump備份時的一致性讀的時機(begin/start transaction 後面的第一條語句)。