對於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操作的時候加上鎖了,其實,這樣做的原因在於可以盡量減少加鎖的影響。
加上全局讀鎖,只允許讀,不允許更新操作。
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相當於在執行完START TRANSACTION後對每個Innodb表執行了SELECT操作。
不明白事務的童鞋可能覺得這點會比較繞,其實所謂的不可重復讀和幻讀可簡單理解為,在同一個事務內,兩次SELECT的結果並不相同。
之所以要使用START TRANSACTION WITH CONSISTENT SNAPSHOT,因為每個表的備份時間並不相同,這就要求在對第一張表進行備份的期間,對第二個表進行的操作,並不會反映到第二張表開始備份時執行的SELECT操作中。(注:mysqldump備份的底層實現即是select * from tab)。而這用START TRANSACTION就無法實現。
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。
總結:
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表空間快照占用的空間。但這樣做,就無法實現對所有表的一致性備份。
參考:
http://tencentdba.com/blog/mysqldump-backup-principle/