mysqldump備份原理及注意事項


關於MySQL熱備,可分為兩種方式:

  1. 邏輯備份

  2. 物理備份

對於前者,常用的工具是MySQL自帶的mysqldump,對於后者,常用的工具是Percona提供的XtraBackup。

對於規模比較小,業務並不繁忙的數據庫,一般都是選擇mysqldump。

那么,mysqldump的備份原理是什么呢?

拋開源碼不談,其實我們可以通過打開general log,查看mysqldump全庫備份時執行的命令來了解mysqldump背后的原理。

只考慮innodb表的情況如下圖所示:
這里寫圖片描述

打開general log

root@ 04:55:  [sbtest]> set global general_log=on;
Query OK, 0 rows affected (0.00 sec)

其中,general log的存放路徑可通過以下命令查看

root@ 04:59:  [sbtest]> show variables like '%general_log_file%';
+------------------+---------------------------+
| Variable_name | Value |
+------------------+---------------------------+

| general_log_file | /data/mysql/localhost.log |
+------------------+---------------------------+

1 row in set (0.00 sec)

執行全庫備份

[root@localhost ~]# mysqldump --master-data=2 -R --single-transaction -A -p123456 > lijingkuan.sql
Warning: Using a password on the command line interface can be insecure.
Warning: A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions, even those that changed suppressed parts of the database. If you don't want to restore GTIDs, pass --set-gtid-purged=OFF. To make a complete dump, pass --all-databases --triggers --routines --events.

其中

–master-data指定為2指的是會在備份文件中生成CHANGE MASTER的注釋。具體在本例中,指的是

-- CHANGE MASTER TO MASTER_LOG_FILE='mybinlog.000008', MASTER_LOG_POS=222448728;

如果該值設置為1,則生成的是CHANGE MASTER的命令,而不是注釋。

-R 備份存儲過程與函數

–single-transaction 獲取InnoDB表的一致性備份。

-A 相當於–all-databases。

下面來看看general log中的內容

170529  5:00:47   215 Connect   root@localhost on 
215 Query /*!40100 SET @@SQL_MODE='' */
215 Query /*!40103 SET TIME_ZONE='+00:00' */
215 Query FLUSH /*!40101 LOCAL */ TABLES
215 Query FLUSH TABLES WITH READ LOCK
215 Query SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ
215 Query START TRANSACTION /*!40100 WITH CONSISTENT SNAPSHOT */
215 Query SHOW VARIABLES LIKE 'gtid\_mode'
215 Query SELECT @@GLOBAL.GTID_EXECUTED
215 Query SHOW MASTER STATUS
215 Query UNLOCK TABLES
215 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 GROUP BY LOGFILE_GROUP_NAME, FILE_NAME, ENGINE ORDER BY LOGFILE_GROUP_NAME
215 Query SELECT DISTINCT TABLESPACE_NAME, FILE_NAME, LOGFILE_GROUP_NAME, EXTENT_SIZE, INITIAL_SIZE, ENGINE FROM INFORMATION_SCHEMA.FILES WHERE FILE_TYPE
= 'DATAFILE' ORDER BY TABLESPACE_NAME, LOGFILE_GROUP_NAME
215 Query SHOW DATABASES
215 Query SHOW VARIABLES LIKE 'ndbinfo\_version'

其中,比較重要的有以下幾點:

1.FLUSH /!40101 LOCAL / TABLES

Closes all open tables, forces all tables in use to be closed, and flushes the query cache.

2.FLUSH TABLES WITH READ LOCK

執行flush tables操作,並加一個全局讀鎖,很多童鞋可能會好奇,這兩個命令貌似是重復的,為什么不在第一次執行flush tables操作的時候加上鎖呢?

下面看看源碼中的解釋:

 /*
We do first a FLUSH TABLES. If a long update is running, the FLUSH TABLES
will wait but will not stall the whole mysqld, and when the long update is
done the FLUSH TABLES WITH READ LOCK will start and succeed quickly. So,
FLUSH TABLES is to lower the probability of a stage where both mysqldump
and most client connections are stalled. Of course, if a second long
update starts between the two FLUSHes, we have that bad stall.
*/

簡而言之,是為了避免較長的事務操作造成FLUSH TABLES WITH READ LOCK操作遲遲得不到鎖,但同時又阻塞了其它客戶端操作。
(flush tables只是關閉所有打開的表,並不獲取鎖。如果沒有長事務,命令會很快執行完成(因為長事務會導致表無法關閉。但長時間未提交的事務不會導致表無法關閉)。長時間未提交的事務也不會阻塞flush tables。flush tables也不會阻塞后續其他客戶端的事務操作;
flush tables with read lock才會獲取讀鎖。長時間未提交的事務不會阻塞FTWRL,但是FTWRL會阻塞活動的事務執行后續的更改和提交等操作(比如長時間未提交的事務提交,或者繼續update),以及阻塞后續的事務開始執行,數據庫處於stall狀態;)

3.SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ

設置當前會話的事務隔離等級為RR,RR可避免不可重復讀和幻讀。

4.START TRANSACTION /!40100 WITH CONSISTENT SNAPSHOT /

獲取當前數據庫的快照,這個是由mysqldump中–single-transaction決定的。

這個只適用於支持事務的表,在MySQL中,只有Innodb。
注意:START TRANSACTION和START TRANSACTION WITH CONSISTENT SNAPSHOT並不一樣,

START TRANSACTION WITH CONSISTENT SNAPSHOT是開啟事務的一致性快照。

下面看看官方的說法,

   The WITH CONSISTENT SNAPSHOT modifier starts a consistent read for storage engines that are capable of it. 
This applies only to InnoDB.
The effect is the same as issuing a START TRANSACTION followed by a SELECT from any InnoDB table.

如何理解呢?

簡而言之,就是開啟事務並對所有表執行了一次SELECT操作,這樣可保證備份時,在任意時間點執行select * from table得到的數據和執行START TRANSACTION WITH CONSISTENT SNAPSHOT時的數據一致。

注意,WITH CONSISTENT SNAPSHOT只在RR隔離級別下有效。

下面通過實例看看START TRANSACTION WITH CONSISTENT SNAPSHOT和START TRANSACTION的不同

注意:session 2是自動提交

START TRANSACTION WITH CONSISTENT SNAPSHOT
這里寫圖片描述

START TRANSACTION
這里寫圖片描述

可見,如果僅是START TRANSACTION,事務2的insert操作提交后,session 1可見(注意,可見的前提是session 2的insert操作在session 1的select操作之前)

而如果是START TRANSACTION WITH CONSISTENT SNAPSHOT,則即便session 2的insert操作在session 1的select操作之前,對session 1均不可見。

5.SHOW MASTER STATUS

這個是由–master-data決定的,記錄了開始備份時,binlog的狀態信息,包括MASTER_LOG_FILE和MASTER_LOG_POS

6.UNLOCK TABLES

釋放鎖。

因為我的數據庫中只有以下五個庫

mysql> show databases;
+--------------------+

| Database |
+--------------------+

| information_schema |
| mysql |
| performance_
schema |
| sbtest |
| test |
+--------------------+

備份的時候可以發現只備份了mysql和test,sbtest,並沒有備份information_schema和performance_schema。

下面來看看備份mysql和test的日志輸出信息,

因日志輸出信息太多,在這里,只選擇sbtest庫的日志信息。test庫中一共有一張表sbtest。

                  215 Init DB   sbtest
215 Query SHOW CREATE DATABASE IF NOT EXISTS `sbtest`
215 Query SAVEPOINT sp
215 Query show tables
215 Query show table status like 'sbtest'
215 Query SET SQL_QUOTE_SHOW_CREATE=1
215 Query SET SESSION character_set_results = 'binary'
215 Query show create table `sbtest`
215 Query SET SESSION character_set_results = 'utf8'
215 Query show fields from `sbtest`
215 Query SELECT /*!40001 SQL_NO_CACHE */ * FROM `sbtest`
170529 5:00:51 215 Query SET SESSION character_set_results = 'binary'
215 Query use `sbtest`
215 Query select @@collation_database
215 Query SHOW TRIGGERS LIKE 'sbtest'
215 Query SET SESSION character_set_results = 'utf8'
215 Query ROLLBACK TO SAVEPOINT sp
215 Query RELEASE SAVEPOINT sp
215 Query use `sbtest`
215 Query select @@collation_database
215 Query SET SESSION character_set_results = 'binary'
215 Query SHOW FUNCTION STATUS WHERE Db = 'sbtest'
215 Query SHOW PROCEDURE STATUS WHERE Db = 'sbtest'
215 Query SET SESSION character_set_results = 'utf8'

從上述輸出可以看出:

  1. 備份的核心是SELECT /!40001 SQL_NO_CACHE / * FROM sbtest語句。

    該語句會查詢到表sbtest的所有數據,在備份文件中會生成相應的insert語句。

    其中SQL_NO_CACHE的作用是查詢的結果並不會緩存到查詢緩存中。

  2. SHOW CREATE DATABASE IF NOT EXISTS sbtest,show create table sbtest

    生成創庫語句和創表語句。

  3. SHOW TRIGGERS LIKE ‘sbtest’

    可以看出,如果不加-R參數,默認是會備份觸發器的。

  4. SHOW FUNCTION STATUS WHERE Db = ‘sbtest’
    SHOW PROCEDURE STATUS WHERE Db = ‘sbtest’

    用於備份存儲過程和函數。

  5. 設置SAVEPOINT,然后備份完每個表后再回滾到該SAVEPOINT。

    為什么要這么做呢?

    前面通過START TRANSACTION WITH CONSISTENT SNAPSHOT開啟的事務只能通過commit或者rollback來結束,而不是ROLLBACK TO SAVEPOINT sp。

    其實,這樣做不會阻塞在備份期間對已經備份表的ddl操作。

/**
ROLLBACK TO SAVEPOINT in --single-transaction mode to release metadata
lock on table which was already dumped. This allows to avoid blocking
concurrent DDL on this table without sacrificing correctness, as we
won't access table second time and dumps created by --single-transaction
mode have validity point at the start of transaction anyway.
Note that this doesn't make --single-transaction mode with concurrent
DDL safe in general case. It just improves situation for people for whom
it might be working.
*/

下面具體來測試一下:

第一種情況:

會話1發起事務,並查詢sbtest表的值,然后會話2進行添加列操作,該操作被hang住。
這里寫圖片描述
第二種情況:

會話1發起事務,然后會話2進行添加列操作,發現該操作成功。
這里寫圖片描述
第三種情況:

模仿mysqldump的備份原理,設置斷點。

注意,DDL操作發起的時間是在執行了select * from test之后,如果是在之前,根據上面第二種情況的測試,是可以進行DDL操作的。

此時,如果不執行ROLLBACK TO SAVEPOINT sp,DDL操作會一直hang下去,執行了該操作后,DDL操作可以繼續執行了。

由此可見,ROLLBACK TO SAVEPOINT確實可以提高DDL的並發性。

但還有一點需要注意,如果DDL操作是發生在select * from test之前,正如第二種情況所演示的,DDL操作會成功,此時,查看test表的數據會報以下錯誤:

root@test 04:32:49 > select * from test;
ERROR 1412 (HY000): Table definition has changed, please retry transaction

對應mysqldump,會報如下錯誤:

mysqldump: Error 1412: Table definition has changed, please retry transaction when dumping table `test` at row: 0

這里寫圖片描述

總結:

  1. mysqldump的本質是通過select * from tab來獲取表的數據的。

  2. START TRANSACTION /!40100 WITH CONSISTENT SNAPSHOT /必須放到FLUSH TABLES WITH READ LOCK和UNLOCK TABLES之間,放到之前會造成START TRANSACTION /!40100 WITH CONSISTENT SNAPSHOT /和FLUSH TABLES WITH READ LOCK之間執行的DML語句丟失,放到之后,會造成從庫重復插入數據。

  3. mysqldump只適合放到業務低峰期做,如果備份的過程中數據操作很頻繁,會造成Undo表空間越來越大,undo表空間默認是放到共享表空間中的,而ibdata的特性是一旦增大,就不會收縮。

  4. mysqldump的效率還是比較低下,START TRANSACTION /!40100 WITH CONSISTENT SNAPSHOT /只能等到所有表備份完后才結束,其實效率比較高的做法是備份完一張表就提交一次,這樣可盡快釋放Undo表空間快照占用的空間。但這樣做,就無法實現對所有表的一致性備份。

  5. 當有大事務正在執行的時候,會等很久。。。

  6. 對於數據庫中非innodb存儲引擎的表,不能保證一致性。主要是指myisam表。加了–single-transaction就能保證innodb的數據是完全一致的,而myisam引擎無法保證,必須加–lock-all-tables。
  7. myisam引擎為什么無法保證在–single-transaction下得到一致性的備份?
    因為它壓根就不支持事務,自然就無法實現上述的過程,雖然添加了–single-transaction參數的myisam表處理過程和上面的完全一致,但是因為不支持事務,在整個dump過程中無法保證可重復讀,無法得到一致性的備份。而innodb在備份過程中,雖然其他線程也在寫數據,但是dump出來的數據能保證是備份開始時那個binlog pos的數據。
  8. myisam引擎也要保證得到一致性的數據的話,他是如何實現的呢?
    它是通過添加–lock-all-tables,這樣在flush tables with read lock后,直到整個dump過程結束,斷開線程后才會unlock tables釋放鎖(沒必要主動發unlock tables指令),整個dump過程其他線程不可寫,從而保證數據的一致性。也就是說沒有第六步6.UNLOCK TABLES。

  9. 5.6的mysqldump利用保存點機制,每備份完一個表就將一個表上的MDL鎖釋放,避免對一張表鎖更長的時間。

  10. 大家可能有一個疑問,為啥備份innodb表之前,就已經將鎖釋放掉了,這實際上是利用了innodb引擎的MVCC機制,開啟快照讀后,就能獲取那個時間的一致的數據,無論需要備份多長時間,直到整個事務結束(commit)為止。

  11. 為什么備份完成后沒有commit操作
    最后並沒有看到commit,因為在整個事務中,其實並沒有修改任何數據,只是為了保證可重復讀得到備份時間點一致性的快照,dump完成后提交不提交應該無所謂了。

  /*
No reason to explicitely COMMIT the transaction, neither to explicitely
UNLOCK TABLES: these will be automatically be done by the server when we
disconnect now. Saves some code here, some network trips, adds nothing to
server.
*/

參考:
1.mysqldump的實現原理(包括部分圖片解釋)
http://www.cnblogs.com/ivictor/p/5505307.html
2.FLUSH TABLE WITH READ LOCK詳解
http://www.cnblogs.com/cchust/p/4603599.html
3.MySQL備份原理詳解
http://www.cnblogs.com/cchust/p/5452557.html


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com