mysqldump的實現原理


對於MySQL的備份,可分為以下兩種:

1. 冷備

2. 熱備

其中,冷備,顧名思義,就是將數據庫關掉,利用操作系統命令拷貝數據庫相關文件。而熱備指的是在線熱備,即在不關閉數據庫的情況下,對數據庫進行備份。實際生產中基本上都是后者。

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

1. 邏輯備份

2. 物理備份

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

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

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

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

 

打開general log

mysql> set global general_log=on;

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

mysql> show variables like '%general_log_file%';

 

執行全庫備份

# mysqldump --master-data=2  -R --single-transaction -A -phello > 3306_20160518.sql

其中

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

-- CHANGE MASTER TO MASTER_LOG_FILE='mysql2-bin.000049', MASTER_LOG_POS=587;

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

-R 備份存儲過程與函數

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

-A 相當於--all-databases。

 

下面來看看general log中的內容

160518 11:00:59    14 Connect   root@localhost on
                   14 Query     /*!40100 SET @@SQL_MODE='' */
                   14 Query     /*!40103 SET TIME_ZONE='+00:00' */
                   14 Query     FLUSH /*!40101 LOCAL */ TABLES
                   14 Query     FLUSH TABLES WITH READ LOCK
                   14 Query     SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ
                   14 Query     START TRANSACTION /*!40100 WITH CONSISTENT SNAPSHOT */
                   14 Query     SHOW VARIABLES LIKE 'gtid\_mode'
                   14 Query     SHOW MASTER STATUS
                   14 Query     UNLOCK TABLES
                   14 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
                   14 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
                   14 Query     SHOW DATABASES
                   14 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操作遲遲得不到鎖,但同時又阻塞了其它客戶端操作。

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 |
| test               |
+--------------------+
4 rows in set (0.03 sec)

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

 

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

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

                   14 Init DB   test
                   14 Query     SHOW CREATE DATABASE IF NOT EXISTS `test`
                   14 Query     SAVEPOINT sp
                   14 Query     show tables

                   14 Query     show table status like 'test'
                   14 Query     SET SQL_QUOTE_SHOW_CREATE=1
                   14 Query     SET SESSION character_set_results = 'binary'
                   14 Query     show create table `test`
                   14 Query     SET SESSION character_set_results = 'utf8'
                   14 Query     show fields from `test`
                   14 Query     SELECT /*!40001 SQL_NO_CACHE */ * FROM `test`
                   14 Query     SET SESSION character_set_results = 'binary'

                   14 Query     use `test`
                   14 Query     select @@collation_database
                   14 Query     SHOW TRIGGERS LIKE 'test'
                   14 Query     SET SESSION character_set_results = 'utf8'
                   14 Query     ROLLBACK TO SAVEPOINT sp

                   14 Query     show table status like 'test1'
                   14 Query     SET SQL_QUOTE_SHOW_CREATE=1
                   14 Query     SET SESSION character_set_results = 'binary'
                   14 Query     show create table `test1`
                   14 Query     SET SESSION character_set_results = 'utf8'
                   14 Query     show fields from `test1`
                   14 Query     SELECT /*!40001 SQL_NO_CACHE */ * FROM `test1`
                   14 Query     SET SESSION character_set_results = 'binary'

                   14 Query     use `test`
                   14 Query     select @@collation_database
                   14 Query     SHOW TRIGGERS LIKE 'test1'
                   14 Query     SET SESSION character_set_results = 'utf8'
                   14 Query     ROLLBACK TO SAVEPOINT sp

                   14 Query     RELEASE SAVEPOINT sp
                   
                   14 Query     use `test`
                   14 Query     select @@collation_database
                   14 Query     SET SESSION character_set_results = 'binary'
                   14 Query     SHOW FUNCTION STATUS WHERE Db = 'test'
                   14 Query     SHOW CREATE FUNCTION `mycat_seq_currval`
                   14 Query     SHOW PROCEDURE STATUS WHERE Db = 'test'
                   14 Query     SET SESSION character_set_results = 'utf8'
                   14 Quit

從上述輸出可以看出:

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

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

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

2. SHOW CREATE DATABASE IF NOT EXISTS `test`,show create table `test1`

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

3. SHOW TRIGGERS LIKE 'test1'

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

4. SHOW FUNCTION STATUS WHERE Db = 'test'

    SHOW CREATE FUNCTION `mycat_seq_currval`

    SHOW PROCEDURE STATUS WHERE Db = 'test'

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

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發起事務,並查詢test表的值,然后會話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. 為什么備份完成后沒有commit操作

  /*
    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.
  */

 

參考:

http://tencentdba.com/blog/mysqldump-backup-principle/

    

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM