mysqldump的實現原理

對於MySQL的備份,可分爲如下兩種:mysql

1. 冷備sql

2. 熱備數據庫

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

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

1. 邏輯備份併發

2. 物理備份app

對於前者,經常使用的工具是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/

相關文章
相關標籤/搜索