【轉】mysqldump與innobackupex知多少

做者:羅小波html

【目錄】mysql

1. 先看mysqldump 
1.1 mysqldump備份過程解讀 
1.2 mysqldump備份過程當中的關鍵步驟 
1.2.1 FLUSH TABLES和FLUSH TABLES WITH READ LOCK的區別 
1.2.2 修改隔離級別的做用 
1.2.3 使用WITH CONSISTENT SNAPSHOT子句的做用 
1.2.4 使用savepoint來設置回滾點的做用 
1.3 mysqldump有什麼坑嗎? 
1.3.1. 坑一 
1.3.2. 坑二 
1.3.3. 有辦法改善這這些問題嗎? 
2. 如今看innobackupex 
2.1 innobackupex備份過程解讀 
2.2 innobackupex爲何須要這麼作 
2.3 innobackupex有什麼坑嗎? 
3. 總結sql

導讀

想必搞數據庫的都知道:數據庫

  • mysqldump優勢:mysqldump的優勢就是邏輯備份,把數據生成SQL形式保存,在單庫,單表數據遷移,備份恢復等場景方便,SQL形式的備份文件通用,也方便在不一樣數據庫之間移植。對於InnoDB表能夠在線備份。緩存

  • mysqldump缺點:mysqldump是單線程,數據量大的時候,備份時間長,甚至有可能在備份過程當中非事務表長期鎖表對業務形成影響(SQL形式的備份恢復時間也比較長)。mysqldump備份時會查詢全部的數據,這可能會把內存中的熱點數據刷掉ruby

  • innobackupex優勢:物理備份能夠繞過MySQL Server層,加上自己就是文件系統級別的備份,備份速度塊,恢復速度快,能夠在線備份,支持併發備份,支持加密傳輸,支持備份限速服務器

  • innobackupex缺點:要提取部分庫表數據比較麻煩,不能按照基於時間點來恢復數據,而且不能遠程備份,只能本地備份,增量備份的恢復也比較麻煩。若是使用innobackupex的全備+binlog增量備份就能夠解決基於時間點恢復的問題。微信

要查看備份過程當中這倆備份工具都對數據庫作了什麼操做,想必你們都知道:能夠打開general_log來查。那麼問題來了,general_log輸出的信息都表明什麼?若是不這樣作會怎樣?這兩個備份工具會不會有什麼平時被忽略的坑?請看下文分析,也許……你會發現原來以前對這倆備份工具好像也不是那麼瞭解!markdown

環境信息session

  • 服務器配置:

    • CPU:4 vcpus
    • 內存:4G
    • 磁盤:250G SAS
    • 網卡:Speed: 1000Mb/s
  • 操做系統:CentOS release 6.5 (Final)

  • 數據庫版本:MySQL 5.7.17

  • xtrabackup版本:2.4.4

  • 主從IP(文中一些演示步驟須要用到主備複製架構):

    • 主庫:192.168.2.111(如下稱爲A庫)
    • 從庫:192.168.2.121(如下稱爲B庫)
  • 數據庫關鍵配置參數

    • 主庫:雙一log_slave_updateslog-binbinlog_rows_query_log_events=ONserver-id=3306111gtid_mode=ONenforce_gtid_consistency=ONauto_increment_increment=2auto_increment_offset=1
    • 備庫:雙一log_slave_updateslog-binbinlog_rows_query_log_events=ONserver-id=3306121gtid_mode=ONenforce_gtid_consistency=ONauto_increment_increment=2auto_increment_offset=2
  • 測試庫表建立(這裏在同一個庫下建立兩個表,一個表爲InnoDB引擎,一個爲MyISAM引擎)

root@localhost : (none) 04:21:27> create database luoxiaobo;
Query OK, 1 row affected (0.01 sec)
root@localhost : (none) 04:21:45> use luoxiaobo
Database changed
root@localhost : luoxiaobo 04:21:55> create table t_luoxiaobo(id int unsigned not null primary key auto_increment,test varchar(50),datet_time datetime) engine=innodb;
Query OK, 0 rows affected (0.05 sec)
root@localhost : luoxiaobo 04:23:00> insert into t_luoxiaobo(test,datet_time) values('1',now());
Query OK, 1 row affected (0.00 sec)
root@localhost : luoxiaobo 04:23:32> insert into t_luoxiaobo(test,datet_time) values('2',now());
Query OK, 1 row affected (0.01 sec)
root@localhost : luoxiaobo 04:23:36> insert into t_luoxiaobo(test,datet_time) values('3',now());
Query OK, 1 row affected (0.00 sec)
root@localhost : luoxiaobo 04:23:38> insert into t_luoxiaobo(test,datet_time) values('4',now());
Query OK, 1 row affected (0.00 sec)
root@localhost : luoxiaobo 04:23:41> select * from t_luoxiaobo;
+----+------+---------------------+
| id | test | datet_time          |
+----+------+---------------------+
|  1 | 1    | 2017-07-01 16:23:32 |
|  3 | 2    | 2017-07-01 16:23:36 |
|  5 | 3    | 2017-07-01 16:23:38 |
|  7 | 4    | 2017-07-01 16:23:41 |
+----+------+---------------------+
4 rows in set (0.00 sec)
root@localhost : luoxiaobo 04:24:51> create table t_luoxiaobo2(id int unsigned not null primary key auto_increment,test varchar(50),datet_time datetime) engine=myisam;
Query OK, 0 rows affected (0.04 sec)
root@localhost : luoxiaobo 05:38:19> insert into t_luoxiaobo2(test,datet_time) values('1',now());
Query OK, 1 row affected (0.01 sec)
root@localhost : luoxiaobo 05:38:29> insert into t_luoxiaobo2(test,datet_time) values('2',now());
Query OK, 1 row affected (0.00 sec)
root@localhost : luoxiaobo 05:38:32> insert into t_luoxiaobo2(test,datet_time) values('3',now());
Query OK, 1 row affected (0.01 sec)
root@localhost : luoxiaobo 05:38:35> insert into t_luoxiaobo2(test,datet_time) values('4',now());
Query OK, 1 row affected (0.00 sec)
root@localhost : luoxiaobo 05:38:37> select * from t_luoxiaobo2;
+----+------+---------------------+
| id | test | datet_time          |
+----+------+---------------------+
|  1 | 1    | 2017-07-01 17:38:29 |
|  3 | 2    | 2017-07-01 17:38:32 |
|  5 | 3    | 2017-07-01 17:38:35 |
|  7 | 4    | 2017-07-01 17:38:37 |
+----+------+---------------------+
4 rows in set (0.00 sec)

1. 先看mysqldump

1.1 mysqldump備份過程解讀

一般,使用mysqldump備份期間,爲了使得數據庫中加鎖時間儘可能短,會使用--single-transaction選項來開啓一個一致性快照事務,爲了使得備份期間可以得到一個與數據一致的binlog pos點,會使用--master-data選項,如今登陸A庫主機,使用這倆選項執行備份演示。

  • 先在數據庫中打開general_log
root@localhost : luoxiaobo 04:23:50> show variables like 'general_log';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| general_log   | OFF   |
+---------------+-------+
1 row in set (0.00 sec)
root@localhost : luoxiaobo 04:24:41> set global general_log=1;
Query OK, 0 rows affected (0.03 sec)
root@localhost : luoxiaobo 04:24:49> show variables like 'general_log';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| general_log   | ON    |
+---------------+-------+
1 row in set (0.01 sec)
  • 使用mysqldump備份(使用strace捕獲執行過程當中的調用棧),這裏緊以備份測試庫luoxiaobo爲例進行演示:
[root@localhost ~]# strace mysqldump -h 192.168.2.111 -uadmin -pletsg0 --single-transaction --master-data=2 --triggers --routines --events luoxiaobo > \
backup_`date +%F_%H_%M_%S`.sql 2> strace_mysqldump.txt
  • 備份完成以後,查看general_log中的內容(去掉了一些無用信息):
* 留意unlock tables語句的位置,是在show master status語句獲取了binlog pos以後當即執行
[root@localhost ~]# cat /home/mysql/data/mysqldata1/mydata/localhost.log 
......
' #修改session級別的sql_mode爲空,避免可能有些sql_mode值對備份產生影響'
2017-07-01T17:42:17.779564+08:00        6 Query /*!40100 SET @@SQL_MODE='' */  2017-07-01T17:42:17.779695+08:00        6 Query /*!40103 SET TIME_ZONE='+00:00' */
' #強制刷新表緩存到磁盤並關閉表(但已經加表鎖的表會阻塞該語句)'
2017-07-01T17:42:17.779889+08:00        6 Query FLUSH /*!40101 LOCAL */ TABLES 
' # 對整個實例加全局讀鎖,若是存在表鎖將阻塞加全局讀鎖語句'
2017-07-01T17:42:17.780047+08:00        6 Query FLUSH TABLES WITH READ LOCK
' #在session級別修改隔離級別爲RR'
2017-07-01T17:42:17.780201+08:00        6 Query SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ
'# 開啓一個一致性快照事務,必須在隔離級別RR下才能開啓一個快照事務'
2017-07-01T17:42:17.780326+08:00        6 Query START TRANSACTION /*!40100 WITH CONSISTENT SNAPSHOT */ 
'#查看是否開啓GTID'
2017-07-01T17:42:17.780452+08:00        6 Query SHOW VARIABLES LIKE 'gtid\_mode' 
'#若是開啓GTID則查看當前的事務GTID集合'
2017-07-01T17:42:17.781867+08:00        6 Query SELECT @@GLOBAL.GTID_EXECUTED 
'#查看當前數據的binlog pos'
2017-07-01T17:42:17.781999+08:00        6 Query SHOW MASTER STATUS  
'#釋放全局讀鎖,留意解鎖的位置,下文會專門提到這個'
2017-07-01T17:42:17.782113+08:00        6 Query UNLOCK TABLES 
......
2017-07-01T17:42:17.786315+08:00        6 Init DB   luoxiaobo
'#在一個數據庫開始備份以前,設置一個保存點(回滾點)'
2017-07-01T17:42:17.786428+08:00        6 Query SAVEPOINT sp  
'#查看庫下有哪些表'
2017-07-01T17:42:17.786539+08:00        6 Query show tables  
' #查看這個表的狀態'
2017-07-01T17:42:17.786710+08:00        6 Query show table status like 't\_luoxiaobo' 
'# 給每一個表的每一個字段加個反引號'
2017-07-01T17:42:17.786908+08:00        6 Query SET SQL_QUOTE_SHOW_CREATE=1 
'#表結構的備份都是binary格式,因此要先改這個'
2017-07-01T17:42:17.787023+08:00        6 Query SET SESSION character_set_results = 'binary' 
' #查看這個表的定義語句'
2017-07-01T17:42:17.787137+08:00        6 Query show create table `t_luoxiaobo` 
'# 修改session的數據結果返回字符集,備份數據須要使用數據本來的字符集,這裏是utf8'
2017-07-01T17:42:17.787329+08:00        6 Query SET SESSION character_set_results = 'utf8' 
' #查看這個表的字段信息'
2017-07-01T17:42:17.787450+08:00        6 Query show fields from `t_luoxiaobo` 
2017-07-01T17:42:17.787715+08:00        6 Query show fields from `t_luoxiaobo`
' #查詢表中的數據,結合show fields from `t_luoxiaobo`的字段信息生成insert into語句'
2017-07-01T17:42:17.787967+08:00        6 Query SELECT /*!40001 SQL_NO_CACHE */ * FROM `t_luoxiaobo` 
2017-07-01T17:42:17.788285+08:00        6 Query SET SESSION character_set_results = 'binary' 
2017-07-01T17:42:17.788411+08:00        6 Query use `luoxiaobo`
2017-07-01T17:42:17.788535+08:00        6 Query select @@collation_database
'#查看是否有這個表的觸發器'
2017-07-01T17:42:17.788668+08:00        6 Query SHOW TRIGGERS LIKE 't\_luoxiaobo'  
2017-07-01T17:42:17.788926+08:00        6 Query SET SESSION character_set_results = 'utf8'
' #t_luoxiaobob表備份結束,回滾到保存點sp,以釋放select *...語句產生的MDL鎖,若是不回滾到sp,後續整個備份過程當中沒法對該表執行DDL操做'
2017-07-01T17:42:17.789043+08:00        6 Query ROLLBACK TO SAVEPOINT sp  
2017-07-01T17:42:17.789191+08:00        6 Query show table status like 't\_luoxiaobo2'
2017-07-01T17:42:17.789399+08:00        6 Query SET SQL_QUOTE_SHOW_CREATE=1
2017-07-01T17:42:17.789510+08:00        6 Query SET SESSION character_set_results = 'binary'
2017-07-01T17:42:17.789625+08:00        6 Query show create table `t_luoxiaobo2`
2017-07-01T17:42:17.789753+08:00        6 Query SET SESSION character_set_results = 'utf8'
2017-07-01T17:42:17.789871+08:00        6 Query show fields from `t_luoxiaobo2`
2017-07-01T17:42:17.790123+08:00        6 Query show fields from `t_luoxiaobo2`
'#備份表t_luoxiaobo2'
2017-07-01T17:42:17.790486+08:00        6 Query SELECT /*!40001 SQL_NO_CACHE */ * FROM `t_luoxiaobo2`  
2017-07-01T17:42:17.790689+08:00        6 Query SET SESSION character_set_results = 'binary'
2017-07-01T17:42:17.790806+08:00        6 Query use `luoxiaobo`
2017-07-01T17:42:17.790923+08:00        6 Query select @@collation_database
2017-07-01T17:42:17.791053+08:00        6 Query SHOW TRIGGERS LIKE 't\_luoxiaobo2'
2017-07-01T17:42:17.791378+08:00        6 Query SET SESSION character_set_results = 'utf8'
'#備份t_luoxiaobo2表過程與t__luoxiaobo表徹底同樣'
2017-07-01T17:42:17.791497+08:00        6 Query ROLLBACK TO SAVEPOINT sp  
'#整個luoxiaobo庫備份完成以後,釋放該保存點'
2017-07-01T17:42:17.791606+08:00        6 Query RELEASE SAVEPOINT sp  
' #查看是否有相關的events'
2017-07-01T17:42:17.791717+08:00        6 Query show events 
2017-07-01T17:42:17.792065+08:00        6 Query use `luoxiaobo`
2017-07-01T17:42:17.792323+08:00        6 Query select @@collation_database
2017-07-01T17:42:17.792489+08:00        6 Query SET SESSION character_set_results = 'binary'
'#查看luoxiaobo庫是否有存儲函數'
2017-07-01T17:42:17.792617+08:00        6 Query SHOW FUNCTION STATUS WHERE Db = 'luoxiaobo' 
' #查看luoxiaobo庫是否有存儲過程'
2017-07-01T17:42:17.793967+08:00        6 Query SHOW PROCEDURE STATUS WHERE Db = 'luoxiaobo'
2017-07-01T17:42:17.794952+08:00        6 Query SET SESSION character_set_results = 'utf8'
'#備份結束,退出鏈接'
2017-07-01T17:42:17.805746+08:00        6 Quit

查看strace抓取的調用棧信息,限於篇幅,詳見爲知筆記連接:

http://5d096a11.wiz03.com/share/s/1t2mEh0a-kl_2c2NZ33kSiac3oxBB40tGQNY2L6Z_M2LtLbG

上面的strace信息是否是看起來和general_log中的信息很像啊?由於general_log中記錄的就是mysqldump發送過去的SQL語句:

從上面general_log和strace信息對比咱們能夠知道,strace信息表明了mysqldump進程對數據庫進程發送了哪些請求信息,general_log表明了數據庫中全部的客戶端SQL請求操做記錄,這就是你們熟知的mysqldump備份過程當中的關鍵步驟,那麼問題來了,mysqldump備份過程當中爲何須要這些 步驟?不這麼作會怎樣?下面對這些步驟逐一使用演示步驟進行詳細解釋。

1.2 mysqldump備份過程當中的關鍵步驟

1.2.1 FLUSH TABLES和FLUSH TABLES WITH READ LOCK的區別

  • FLUSH TABLES

強制關閉全部正在使用的表,並刷新查詢緩存,從查詢緩存中刪除全部查詢緩存結果,相似RESET QUERY CACHE語句的行爲

在MySQL 5.7官方文檔描述中,當有表正處於LOCK TABLES … READ語句加鎖狀態時,不容許使用FLUSH TABLES語句(另一個會話執行FLUSH TABLES會被阻塞),若是已經使用LOCK TABLES … READ語句對某表加讀鎖的狀況下要對另外的表執行刷新,能夠在另一個會話中使用FLUSH TABLES tbl_name … WITH READ LOCK語句(稍後會講到)

注意:

  • 若是一個會話中使用LOCK TABLES語句對某表加了表鎖,在該表鎖未釋放前,那麼另一個會話若是執行FLUSH TABLES語句會被阻塞
  • 若是一個會話正在執行DDL語句,那麼另一個會話若是執行FLUSH TABLES 語句會被阻塞
  • 若是一個會話正在執行DML大事務(DML語句正在執行,數據正在發生修改,而不是使用lock in share mode和for update語句來顯式加鎖),那麼另一個會話若是執行FLUSH TABLES語句會被阻塞
  • FLUSH TABLES WITH READ LOCK

關閉全部打開的表,並使用全局讀鎖鎖定整個實例下的全部表。此時,你能夠方便地使用支持快照的文件系統進行快照備份,備份完成以後,使用UNLOCK TABLES語句釋放鎖。

FLUSH TABLES WITH READ LOCK語句獲取的是一個全局讀鎖,而不是表鎖,所以表現行爲不會像LOCK TABLES和UNLOCK TABLES語句,LOCK TABLES和UNLOCK TABLES語句在與事務混搭時,會出現一些相互影響的狀況,以下:

  • 若是有表使用了LOCK TABLES語句加鎖,那麼開啓一個事務會形成該表的表鎖被釋放(注意是任何表的表鎖,只要存在表鎖都會被釋放,另外,必須是同一個會話中操做纔會形成這個現象),就相似執行了UNLOCK TABLES語句同樣,但使用FLUSH TABLES WITH READ LOCK語句加全局讀鎖,開啓一個事務不會形成全局讀鎖被釋放

  • 若是你開啓了一個事務,而後在事務內使用LOCK TABLES語句加鎖和FLUSH TABLES WITH READ LOCK語句加全局讀鎖(注意,是對任何表加表鎖,只要使用了LOCK TABLES),會形成該事務隱式提交

  • 若是你開啓了一個事務,而後在事務內使用UNLOCK TABLES語句,無效

  • 官方文檔中還有一句:」若是有表使用LOCK TABLES語句加表鎖,在使用UNLOCK TABLES語句解鎖時會形成該表的全部事務隱式提交」,我的認爲這是理論上的說法,或者說本人能力有限,暫未想到可能會形成這種狀況的緣由,由於實際上使用LOCK TABLES語句語句時,開啓一個事務會形成自動解鎖(前面已經提到過),而若是在事務內使用LOCK TABLES語句會形成事務隱式提交(前面已經提到過),因此實際上不可能出如今事務內使用UNLOCK TABLES語句解鎖LOCK TABLES語句的狀況,而若是是使用FLUSH TABLES WITH READ LOCK語句,若是執行該語句以前存在LOCK TABLES加的表鎖,則FLUSH TABLES WITH READ LOCK語句發生阻塞,若是是已經執行FLUSH TABLES WITH READ LOCK語句,LOCK TABLES語句發生阻塞,不會再有任何的表鎖和互斥鎖可以被獲取到(新的非select和show的請求都會被阻塞)。因此不可能出現UNLOCK TABLES語句解鎖時形成隱式提交

注:

  • FLUSH TABLES WITH READ LOCK語句不會阻塞日誌表的寫入,例如:查詢日誌,慢查詢日誌等
  • FLUSH TABLES WITH READ LOCK語句與XA協議不兼容
  • 若是一個會話中使用LOCK TABLES語句對某表加了表鎖,在該表鎖未釋放前,那麼另一個會話若是執行FLUSH TABLES WITH READ LOCK語句會被阻塞,而若是數據庫中lock_wait_timeout參數設置時間過短,mysqldump將會由於執行FLUSH TABLES WITH READ LOCK語句獲取全局讀鎖超時而致使備份失敗退出
  • 若是一個會話正在執行DDL語句,那麼另一個會話若是執行FLUSH TABLES WITH READ LOCK語句會被阻塞,若是數據庫中lock_wait_timeout參數設置時間過短,mysqldump將會由於執行FLUSH TABLES WITH READ LOCK語句獲取全局讀鎖超時而致使備份失敗退出
  • 若是一個會話正在執行DML大事務(DML語句正在執行,數據正在發生修改,而不是使用lock in share mode和for update語句來顯式加鎖),那麼另一個會話若是執行FLUSH TABLES WITH READ LOCK語句會被阻塞,若是數據庫中lock_wait_timeout參數設置時間過短,mysqldump將會由於執行FLUSH TABLES WITH READ LOCK語句獲取全局讀鎖超時而致使備份失敗退出
  • FLUSH TABLES tbl_name [,tbl_name] … WITH READ LOCK

刷新表並獲取指定表的讀鎖。該語句首先獲取表的獨佔MDL鎖,因此須要等待該表的全部事務提交完成。而後刷新該表的表緩存,從新打開表,獲取表讀鎖(相似LOCK TABLES … READ),並將MDL鎖從獨佔級別降級爲共享。在該語句獲取表讀鎖、降級MDL鎖以後,其餘會話能夠讀取該表,但不能修改表數據及其表結構。

執行該語句須要RELOAD和LOCK TABLES權限。

該語句僅適用於基表(持久表),不適用於臨時表,會自動忽略,另外在對視圖使用該語句使會報錯。

與LOCK TABLES語句相似,在使用該語句對某表加鎖以後,再同一個會話中開啓一個事務時,會被自動解鎖

MySQL5.7官方文檔描述說:這種新的變體語法可以使得只針對某一個表加讀鎖的同時還可以同時刷新這個表,這解決了某表使用LOCK TABLES … READ語句加讀鎖時,須要刷新表不能使用FLUSH TABLES語句的問題,此時可使用FLUSH TABLES tbl_name [,tbl_name] … WITH READ LOCK語句代替,可是,官方描述不太清晰,實測在同一個會話中使用LOCK TABLES … READ語句加讀鎖時,不容許執行該語句(不管操做表是不是同一張表),會報錯:ERROR 1192 (HY000): Can’t execute the given command because you have active locked tables or an active transaction,可是若是在不一樣的會話中,那麼,若是表不相同,容許執行,表相同,則FLUSH TABLES tbl_name [,tbl_name] … WITH READ LOCK語句發生等待

該語句同一個會話重複執行時,不管是否同一個表,都會報錯:ERROR 1192 (HY000): Can't execute the given command because you have active locked tables or an active transactio,若是是不一樣會話不一樣表則容許執行,可是表相同則發生等待

1.2.2 修改隔離級別的做用

爲何要執行SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ語句呢?由於後續須要使用START TRANSACTION /!40100 WITH CONSISTENT SNAPSHOT語句開啓一個一致性事務快照,根據事務一致性讀要求,一致性事務快照只支持RR隔離級別,在其餘隔離級別下執行語句START TRANSACTION /!40100 WITH CONSISTENT SNAPSHOT會報以下警告信息:

'# RU、RC、串行隔離級別報同樣的警告,告訴你WITH CONSISTENT SNAPSHOT子句被忽略,該子句只支持RR隔離級別'
root@localhost : (none) 02:54:15> show variables like '%isolation%';
+---------------+----------------+
| Variable_name | Value          |
+---------------+----------------+
| tx_isolation  | READ-COMMITTED |
+---------------+----------------+
1 row in set (0.00 sec)
root@localhost : (none) 02:54:35> START TRANSACTION WITH CONSISTENT SNAPSHOT;
Query OK, 0 rows affected, 1 warning (0.00 sec)
Warning (Code 138): InnoDB: WITH CONSISTENT SNAPSHOT was ignored because this phrase can only be used with REPEATABLE READ isolation level.

1.2.3 使用WITH CONSISTENT SNAPSHOT子句的做用

START TRANSACTION語句使用WITH CONSISTENT SNAPSHOT子句時,會爲事務啓動一致性讀(該子句僅適用於InnoDB)。其行爲與執行START TRANSACTION語句以後+一個SELECT語句效果相同(會獲取一個事務號,在read view中佔個坑,可是不會請求任何鎖)。WITH CONSISTENT SNAPSHOT子句不會自動修改當前的事務隔離級別,因爲WITH CONSISTENT SNAPSHOT子句要求必須RR隔離級別下才會自動啓用,所以只有當前隔離級別爲RR時纔會啓用一致性快照,非RR隔離級別下,會忽略WITH CONSISTENT SNAPSHOT子句。從MySQL 5.7.2起,當WITH CONSISTENT SNAPSHOT子句被忽略時,會產生一個警告(相似上一篇mysqldump與innobackupex備份過程你知多少(一)提到的警告信息)。

爲了使得更清晰地瞭解mysqldump在備份過程當中使用WITH CONSISTENT SNAPSHOT子句的做用,下面我們來演示一下帶與不帶WITH CONSISTENT SNAPSHOT子句會發生什麼?

  • 開啓兩個會話,操做同一張表






從上面的表格對比結果中能夠看到:

  • WITH CONSISTENT SNAPSHOT子句的做用就至關於START TRANSACTION+ SELECT語句,目地是爲了在開啓事務的那一刻往mvcc的read view中當即加入這個事務,就好像read view在事務一開始就被固定了同樣,使得後續其餘事務的DML不會影響到該事務的查詢結果,這就是所說的一致性讀

  • 若是不使用WITH CONSISTENT SNAPSHOT子句,在使用START TRANSACTION語句顯式開啓一個事務以後,在執行SELECT語句以前,這段時間內若是有別的事務發起了DML操做,就會致使該事務查詢該表的時候讀取的數據與事務開始時間點不一致。

1.2.4 使用savepoint來設置回滾點的做用

你們都知道,設置SAVEPOINT是爲了回滾在設置這個點時候發生變動的數據,可是mysqldump備份只是使用select語句作查詢,爲何要使用savepoint呢?須要回滾什麼呢?請看下文分析:

  • SAVEPOINT 'identifier'語句,爲事務設置一個命名的事務保存點(回滾點),該字符串爲事務保存點的標識符。

  • ROLLBACK TO SAVEPOINT語句的做用是將事務回滾到指定的保存點的位置,而不終止事務。當前事務在回滾點以後的修改的行數據將被撤銷(注:InnoDB不會釋放這些發生修改且被撤銷行的行鎖,注意是修改,不是新插入,這些發生修改的數據行行鎖被存儲在內存中),對於設置了保存點以後,新插入的行數據也會被撤銷(注:這些鎖信息被存儲在行數據中的事務ID上,這些行鎖不會單獨存儲在內存中,在這種狀況下,這些新插入的行數據在被回滾以後,對應的行鎖將被釋放)。另外,回滾到某個保存點以後,比這個保存點在時間上更晚設置的保存點將被刪除。

  • ROLLBACK TO SAVEPOINT語句還有一個做用,能夠釋放在設置保存點以後事務持有的MDL鎖,這點即是mysqldump須要使用保存點的關鍵點。

爲了更清晰地瞭解mysqldump在備份過程當中使用SAVEPOINT sp + ROLLBACK TO SAVEPOINT sp語句的做用,下面使用兩個會話演示一下使用與不使用保存點會發生什麼?






從上面的對比結果中能夠得知:

  • mysqldump使用savepoint的做用就是,當一個顯式開啓的事務回滾到保存點時,除了回滾數據變動以外,還會釋放保存點以後select語句獲取的MDL鎖,使得其餘會話的DDL語句能夠正常執行。對於mysqldump來講,select 語句執行完成以後就表明着該表的數據已經備份完成,無需再繼續持有MDL鎖,使用savepoint就實現了在select 執行完成以後釋放MDL鎖的目的(注:在事務內,執行select *語句雖然不會有數據行鎖,可是會持有表的MDL鎖)。

  • with consistent snapshot子句對應mysqldump實現一致性備份來講相當重要,不只僅是數據的一致性,使用該子句時,表定義也保持事務開啓的那一刻,因此,從上面的對比結果中能夠看到,使用了with consistent snapshot子句開啓一個一致性快照事務以後,若是一旦表結構定義發生改變,事務將沒法重複查詢表。

  • 從上面的演示過程當中,咱們也能夠看到,使用 with consistent snapshot子句顯式開啓一個事務以後,若是該事務沒有對任何表作任何操做時,此時是沒有得到任何鎖的,因此,若是在該事務對某表執行操做以前其餘事務對該表執行了DDL操做以後,將致使該事務沒法再對錶執行查詢,會報表結構發生變化的錯誤;固然,若是顯式開啓事務後當即對某表執行查詢,那麼其餘會話的DDL是會發生阻塞的;當在該事務使用savepoint實現方式釋放表的MDL鎖以後,其餘會話容許執行DDL,可是執行了DDL語句以後,該事務就沒法再對該表執行查詢。固然,若是不使用 with consistent snapshot子句,則其餘會話執行的DDL對錶定義的變動不會影響到該事務重複對錶執行查詢。

1.3 mysqldump有什麼坑嗎?

想必你們都知道,mysqldump備份時可使用--single-transaction + --master-data兩個選項執行備份(老實講,爲圖方便,本人以前很長一段時間,生產庫也是使用mysqldudmp遠程備份的),這樣備份過程當中既能夠儘可能不鎖表,也能夠獲取到binlog pos位置,備份文件能夠用於數據恢復,也能夠用於搭建備庫。看起來那麼美好,然而……其實一不當心你就發現踩到坑了

1.3.1 坑一

使用--single-transaction + --master-data時,myisam表持續不斷插入,並用於搭建備庫。

首先在A庫上把myisam表的數據行數弄到100W以上:

...... root@localhost : luoxiaobo 11:26:42> insert into t_luoxiaobo2(test,datet_time) select test,now() from t_luoxiaobo2; Query OK, 1572864 rows affected (4.47 sec) Records: 1572864 Duplicates: 0 Warnings: 0 root@localhost : luoxiaobo 11:26:47> select count(*) from t_luoxiaobo2; +----------+ | count(*) | +----------+ | 3145728 | +----------+ 1 row in set (0.00 sec)

A庫新開一個ssh會話2,使用以下腳本持續對錶t_luoxiaobo2進行插入操做(該表爲myisam表),限於篇幅,請點擊此處獲取。

A庫新開一個ssh會話3,清空查詢日誌:

[root@localhost ~]# echo > /home/mysql/data/mysqldata1/mydata/localhost.log 

如今,A庫在ssh會話3中,使用mysqldump備份整個實例:

[root@localhost ~]# mysqldump -h 192.168.2.111 -uadmin -pletsg0 --single-transaction --master-data=2 --triggers --routines --events -A >\ backup_`date +%F_%H_%M_%S`.sql mysqldump: [Warning] Using a password on the command line interface can be insecure. [root@localhost ~]# ls -lh backup_2017-07-03_00_47_50.sql -rw-r--r-- 1 root root 112M 7月 3 00:47 backup_2017-07-03_00_47_50.sql

備份完成以後,A庫在ssh會話2中,中止持續造數腳本。

A庫在ssh會話2中,查看備份文件中的binlog pos:

[root@localhost ~]# head -100 backup_*.sql |grep -i 'change master to' -- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000005', MASTER_LOG_POS=76657819;

A庫在ssh會話3中,查看查詢日誌,能夠發如今UNLOCK TABLES以後,select *…t_luoxiaobo2表以前,還有數據插入到該表中:

2017-07-03T00:47:50.366670+08:00 87364 Query SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ 2017-07-03T00:47:50.366795+08:00 87363 Query insert into t_luoxiaobo2(test,datet_time) values(11377,now()) 2017-07-03T00:47:50.366862+08:00 87364 Query START TRANSACTION /*!40100 WITH CONSISTENT SNAPSHOT */ 2017-07-03T00:47:50.367023+08:00 87364 Query SHOW VARIABLES LIKE 'gtid\_mode' 2017-07-03T00:47:50.372331+08:00 87364 Query SELECT @@GLOBAL.GTID_EXECUTED 2017-07-03T00:47:50.372473+08:00 87364 Query SHOW MASTER STATUS 2017-07-03T00:47:50.372557+08:00 87364 Query UNLOCK TABLES ...... 2017-07-03T00:47:50.381256+08:00 87366 Query insert into t_luoxiaobo2(test,datet_time) values(11383,now()) ...... 2017-07-03T00:47:50.381577+08:00 87365 Query insert into t_luoxiaobo2(test,datet_time) values(11380,now()) 2017-07-03T00:47:50.381817+08:00 87360 Init DB luoxiaobo 2017-07-03T00:47:50.381886+08:00 87360 Query insert into t_luoxiaobo2(test,datet_time) values(11382,now()) ...... 2017-07-03T00:47:50.391873+08:00 87364 Query show fields from `t_luoxiaobo2` 2017-07-03T00:47:50.392116+08:00 87364 Query show fields from `t_luoxiaobo2` 2017-07-03T00:47:50.392339+08:00 87364 Query SELECT /*!40001 SQL_NO_CACHE */ * FROM `t_luoxiaobo2`

如今,咱們將這個備份文件用於B庫上搭建備庫,並啓動複製,能夠發現有以下複製報錯:

root@localhost : (none) 12:59:12> show slave status\G; *************************** 1\. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.2.111 Master_User: qfsys Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000005 Read_Master_Log_Pos: 79521301 Relay_Log_File: mysql-relay-bin.000002 Relay_Log_Pos: 320 Relay_Master_Log_File: mysql-bin.000005 Slave_IO_Running: Yes Slave_SQL_Running: No ...... Last_SQL_Errno: 1062 Last_SQL_Error: Could not execute Write_rows event on table luoxiaobo.t_luoxiaobo2; Duplicate entry '6465261' for key 'PRIMARY', Error_code: 1062;\ handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mysql-bin.000005, end_log_pos 76658175 ...... ERROR: No query specified

從上面的結果中能夠看到,主鍵衝突了,也就是說備份的表t_luoxiaobo2中的數據與備份文件中獲取的binlog pos點並不一致,我們如今在B庫中,查詢一下這個表中大於等於這個衝突主鍵的數據,從下面的結果中能夠看到,備份文件中若是嚴格按照一致性要求,備份文件中的數據必須和binlog pos點一致,可是如今,備份文件中的數據卻比獲取的binlog pos點多了5行數據:

root@localhost : (none) 12:59:24> use luoxiaobo Database changed root@localhost : luoxiaobo 12:59:44> select id from t_luoxiaobo2 where id>=6465261; +---------+ | id | +---------+ | 6465261 | | 6465263 | | 6465265 | | 6465267 | | 6465269 | +---------+ 5 rows in set (0.01 sec)

如今,我們去掉--single-transaction選項,從新執行本小節以上步驟,從新搭建從庫,看看是否還有問題(這裏限於篇幅,步驟省略,只貼出最後結果):

root@localhost : (none) 01:09:12> change master to master_host='192.168.2.111',master_user='qfsys',master_password='letsg0',master_log_file='mysql-bin.000005',\ master_log_pos=83601517; Query OK, 0 rows affected, 2 warnings (0.02 sec) Note (Code 1759): Sending passwords in plain text without SSL/TLS is extremely insecure. Note (Code 1760): Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider \ using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information. root@localhost : (none) 01:09:23> start slave; Query OK, 0 rows affected (0.01 sec) root@localhost : (none) 01:09:25> show slave status\G; *************************** 1\. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.2.111 Master_User: qfsys Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000005 Read_Master_Log_Pos: 84697699 Relay_Log_File: mysql-relay-bin.000002 Relay_Log_Pos: 1096502 Relay_Master_Log_File: mysql-bin.000005 Slave_IO_Running: Yes Slave_SQL_Running: Yes ...... Exec_Master_Log_Pos: 84697699 ......

從上面的show slave status輸出信息中咱們能夠看到,去掉了--single-transaction選項以後的備份,用於搭建備庫就正常了。另外,咱們從新在A庫上查看查詢日誌也能夠發現,只搜索到flush語句而沒有搜索到unlock tables、set session transaction.. 、start transaction.. 語句,說明備份過程沒有開啓一致性快照事務,沒有修改隔離級別,是全程加全局讀鎖的,mysqldump備份進程結束退出以後mysql server自動回收鎖資源:

[root@localhost ~]# grep -iE 'flush|unlock tables|transaction' /home/mysql/data/mysqldata1/mydata/localhost.log 2017-07-03T01:07:08.195470+08:00 102945 Query FLUSH /*!40101 LOCAL */ TABLES 2017-07-03T01:07:08.206607+08:00 102945 Query FLUSH TABLES WITH READ LOCK

也許你會說,咱們數據庫環境很規範,沒有myisam表,不會有這個問題,OK,贊一個。

1.3.2 坑二

使用--single-transaction + --master-data時,InnoDB表執行online ddl,備份文件用於搭建備庫(注意,本小節中的數據庫實例與前一小節不一樣)。

此次咱們操做InnoDB表,在A庫上先把t_luoxiaobo表的數據也弄到幾百萬行。

qogir_env@localhost : luoxiaobo 10:03:35> insert into t_luoxiaobo(test,datet_time) select test,now() from t_luoxiaobo; Query OK, 1048576 rows affected (9.83 sec) Records: 1048576 Duplicates: 0 Warnings: 0 qogir_env@localhost : luoxiaobo 10:03:46> select count(*) from t_luoxiaobo; +----------+ | count(*) | +----------+ | 2097152 | +----------+ 1 row in set (0.39 sec)

A庫在ssh會話2中,使用以下腳本持續對錶t_luoxiaobo進行DDL操做(該表爲InnoDB表),限於篇幅,請點擊此處獲取。

A庫在ssh會話3中,清空查詢日誌:

[root@localhost ~]# echo > /home/mysql/data/mysqldata1/mydata/localhost.log 

如今,A庫在ssh會話3中,使用mysqldump備份整個實例:

[root@localhost ~]# mysqldump -h 192.168.2.111 -uadmin -pletsg0 --single-transaction --master-data=2 --triggers --routines --events -A > backup_`date +%F_%H_%M_%S`.sql mysqldump: [Warning] Using a password on the command line interface can be insecure. [root@5f1772e3-0c7a-4537-97f9-9b57cf6a04c2 ~]# ls -lh backup_2017-07-03_12_46_49.sql -rw-r--r-- 1 root root 74M Jul 3 12:46 backup_2017-07-03_12_46_49.sql

A庫在ssh會話2中,中止DDL添加腳本。

A庫在ssh會話2中,查看備份文件中的binlog pos:

[root@5f1772e3-0c7a-4537-97f9-9b57cf6a04c2 ~]# head -100 backup_*.sql |grep -i 'change master to' -- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000257', MASTER_LOG_POS=62109599;

如今,咱們將這個備份文件用於在B庫中搭建備庫,並啓動複製,從下面的結果中能夠看到,複製狀態正常:

qogir_env@localhost : (none) 01:32:00> show slave status\G; ...... Master_Log_File: mysql-bin.000257 Read_Master_Log_Pos: 62110423 Relay_Log_File: mysql-relay-bin-channel@002d241.000002 Relay_Log_Pos: 1144 Relay_Master_Log_File: mysql-bin.000257 Slave_IO_Running: Yes Slave_SQL_Running: Yes ...... Exec_Master_Log_Pos: 62110423 ...... Seconds_Behind_Master: 0 ...... Retrieved_Gtid_Set: 799ef59c-4126-11e7-83ce-00163e407cfb:53831090-53831093 Executed_Gtid_Set: 799ef59c-4126-11e7-83ce-00163e407cfb:1-53831093, f9b1a9b6-46b7-11e7-9e8b-00163e4fde29:1 Auto_Position: 0 ......

如今咱們回到A庫上,對錶t_luoxiaobo插入一些測試數據:

qogir_env@localhost : luoxiaobo 12:43:30> insert into t_luoxiaobo(test,datet_time) values('test_replication',now()); Query OK, 1 row affected (0.00 sec) qogir_env@localhost : luoxiaobo 01:36:31> select * from t_luoxiaobo where test='test_replication'; +---------+------------------+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ | id | test | datet_time | test1 | test2 | test3 | test4 | test5 | test6 | test8 | test7 | test9 | +---------+------------------+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ | 7470943 | test_replication | 2017-07-03 13:36:31 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | +---------+------------------+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ 1 row in set (0.96 sec)

在B庫上查詢複製狀態和表t_luoxiaobo中的數據:

qogir_env@localhost : luoxiaobo 01:32:21> show slave status\G; ...... Master_Log_File: mysql-bin.000257 Read_Master_Log_Pos: 62110862 Relay_Log_File: mysql-relay-bin-channel@002d241.000002 Relay_Log_Pos: 1583 Relay_Master_Log_File: mysql-bin.000257 Slave_IO_Running: Yes Slave_SQL_Running: Yes ...... Exec_Master_Log_Pos: 62110862 ...... Seconds_Behind_Master: 0 ...... Retrieved_Gtid_Set: 799ef59c-4126-11e7-83ce-00163e407cfb:53831090-53831094 Executed_Gtid_Set: 799ef59c-4126-11e7-83ce-00163e407cfb:1-53831094, f9b1a9b6-46b7-11e7-9e8b-00163e4fde29:1 ...... 1 row in set (0.00 sec) qogir_env@localhost : luoxiaobo 01:38:23> select * from t_luoxiaobo where test='test_replication'; +---------+------------------+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ | id | test | datet_time | test1 | test2 | test3 | test4 | test5 | test6 | test8 | test7 | test9 | +---------+------------------+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ | 7470943 | test_replication | 2017-07-03 13:36:31 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | +---------+------------------+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ 1 row in set (0.05 sec)

到這裏,看起來一切正常,對不對?開心嗎?先等等,請保持DBA一向嚴謹的優良傳統,我們在主庫上使用pt-table-checksum工具檢查一下:

# 刪除了一部分無用信息,只保留了咱們以前造數的兩張表 [root@5f1772e3-0c7a-4537-97f9-9b57cf6a04c2 ~]# pt-table-checksum --nocheck-replication-filters --no-check-binlog-format --replicate=xiaoboluo.checksums h=localhost,u=admin,p=letsg0,P=3306 TS ERRORS DIFFS ROWS CHUNKS SKIPPED TIME TABLE 07-03T13:57:48 0 16 2097153 18 0 7.543 luoxiaobo.t_luoxiaobo 07-03T13:57:57 0 0 2097152 18 0 9.281 luoxiaobo.t_luoxiaobo2 ......

從上面的信息中能夠看到,表luoxiaobo.t_luoxiaobo的檢測DIFFS 列爲16,表明主從有數據差別,神馬狀況?別急,我們先來分別在AB庫查詢下這張表的數據行數,從下面的結果能夠看到,該表主從數據差別2097152行!!

# A庫 qogir_env@localhost : (none) 01:57:03> use luoxiaobo Database changed qogir_env@localhost : luoxiaobo 02:09:40> select count(*) from t_luoxiaobo; +----------+ | count(*) | +----------+ | 2097153 | +----------+ 1 row in set (0.41 sec) B庫 qogir_env@localhost : (none) 01:55:28> use luoxiaobo Database changed qogir_env@localhost : luoxiaobo 02:10:30> select count(*) from t_luoxiaobo; +----------+ | count(*) | +----------+ | 1 | +----------+ 1 row in set (0.00 sec)

發生什麼了?也許你會說,平時使用mysqldump不都是這樣的嗎?沒毛病啊。回想一下,從我們上篇《mysqldump與innobackupex備份過程知多少(二)》中提到的「WITH CONSISTENT SNAPSHOT語句的做用」時的演示過程能夠知道,DDL的負載是刻意加上去的,還記得以前演示mysqldump使用savepoint的做用的時候,使用start transaction with consistent snapshot語句顯式開啓一個事務以後,該事務執行select以前,該表被其餘會話執行了DDL以後沒法查詢數據,咱們知道mysqldump備份數據的時候,就是在start transaction with consistent snapshot語句開啓的一個一致性快照事務下使用select語句查詢數據進行備份的。爲了證明這個問題,下面咱們打開查詢日誌查看一下在start transaction with consistent snapshot語句和select … 之間是否有DDL語句,以下:

.......

2017-07-03T12:46:57.082727+08:00 1649664 Query SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ 2017-07-03T12:46:57.082889+08:00 1649664 Query START TRANSACTION /*!40100 WITH CONSISTENT SNAPSHOT */ ...... 2017-07-03T12:46:57.085821+08:00 1649664 Query SELECT @@GLOBAL.GTID_EXECUTED 2017-07-03T12:46:57.085954+08:00 1649664 Query SHOW MASTER STATUS ...... ' # 這裏能夠看到,在START TRANSACTION /*!40100 WITH CONSISTENT SNAPSHOT */語句以後,select 備份表t_luoxiaobo表以前,有一個DDL語句插入進來' 2017-07-03T12:46:57.089833+08:00 1649667 Query alter table t_luoxiaobo add column test8 varchar(10) 2017-07-03T12:46:57.095153+08:00 1649664 Query UNLOCK TABLES ...... 2017-07-03T12:46:57.098199+08:00 1649664 Init DB luoxiaobo 2017-07-03T12:46:57.098273+08:00 1649664 Query SHOW CREATE DATABASE IF NOT EXISTS `luoxiaobo` 2017-07-03T12:46:57.098360+08:00 1649664 Query SAVEPOINT sp 2017-07-03T12:46:57.098435+08:00 1649664 Query show tables 2017-07-03T12:46:57.098645+08:00 1649664 Query show table status like 't\_luoxiaobo' 2017-07-03T12:46:57.098843+08:00 1649664 Query SET SQL_QUOTE_SHOW_CREATE=1 2017-07-03T12:46:57.098927+08:00 1649664 Query SET SESSION character_set_results = 'binary' 2017-07-03T12:46:57.099013+08:00 1649664 Query show create table `t_luoxiaobo` 2017-07-03T12:46:57.105056+08:00 1649664 Query SET SESSION character_set_results = 'utf8' 2017-07-03T12:46:57.105165+08:00 1649664 Query show fields from `t_luoxiaobo` 2017-07-03T12:46:57.105538+08:00 1649664 Query show fields from `t_luoxiaobo` '# 這裏本來應該是一句:SELECT /*!40001 SQL_NO_CACHE */ * FROM `t_luoxiaobo`,可是卻沒有,咱們能夠推理一下,由於select的時候報了表定'\ '# 義已經發生變化的錯誤,因此這句select並無被記錄到查詢日誌中來' 2017-07-03T12:46:57.105857+08:00 1649664 Query SET SESSION character_set_results = 'binary' 2017-07-03T12:46:57.105929+08:00 1649664 Query use `luoxiaobo` 2017-07-03T12:46:57.106021+08:00 1649664 Query select @@collation_database 2017-07-03T12:46:57.106116+08:00 1649664 Query SHOW TRIGGERS LIKE 't\_luoxiaobo' 2017-07-03T12:46:57.106394+08:00 1649664 Query SET SESSION character_set_results = 'utf8' 2017-07-03T12:46:57.106466+08:00 1649664 Query ROLLBACK TO SAVEPOINT sp 2017-07-03T12:46:57.106586+08:00 1649664 Query show table status like 't\_luoxiaobo2' ...... 2017-07-03T12:46:57.107063+08:00 1649664 Query show create table `t_luoxiaobo2` 2017-07-03T12:46:57.107151+08:00 1649664 Query SET SESSION character_set_results = 'utf8' 2017-07-03T12:46:57.107233+08:00 1649664 Query show fields from `t_luoxiaobo2` 2017-07-03T12:46:57.107511+08:00 1649664 Query show fields from `t_luoxiaobo2` 2017-07-03T12:46:57.107807+08:00 1649664 Query SELECT /*!40001 SQL_NO_CACHE */ * FROM `t_luoxiaobo2` ......

如今,咱們打開備份文件,找到表t_luoxiaob的備份語句位置,能夠看到並無生成INSERT語句:

DROP TABLE IF EXISTS `t_luoxiaobo`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `t_luoxiaobo` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `test` varchar(50) COLLATE utf8_bin DEFAULT NULL, `datet_time` datetime DEFAULT NULL, `test1` varchar(10) COLLATE utf8_bin DEFAULT NULL, `test2` varchar(10) COLLATE utf8_bin DEFAULT NULL, `test3` varchar(10) COLLATE utf8_bin DEFAULT NULL, `test4` varchar(10) COLLATE utf8_bin DEFAULT NULL, `test5` varchar(10) COLLATE utf8_bin DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=7470943 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; /*!40101 SET character_set_client = @saved_cs_client */; '# 正常狀況下,在這個位置,應該出現LOCK TABLES `t_luoxiaobo` WRITE; + /*!40000 ALTER TABLE `t_luoxiaobo2` DISABLE KEYS */; + INSERT INTO語句的,然而,如今這裏倒是空的' -- -- Table structure for table `t_luoxiaobo2` -- DROP TABLE IF EXISTS `t_luoxiaobo2`; ...... LOCK TABLES `t_luoxiaobo2` WRITE; /*!40000 ALTER TABLE `t_luoxiaobo2` DISABLE KEYS */; INSERT INTO `t_luoxiaobo2` VALUES (1,'1','2017-07-03 09:22:16'),(4,'2','2017-07-03 09:22:19'),(7,'3','2017-07-03 09:22:21'), ......

到這裏,是否是忽然心絃一緊呢?so..若是你決定繼續使用mysqldump,那麼之後搭建好備庫以後,必定要記得校驗一下主備數據一致性!!

1.3.3 有辦法改善這這些問題嗎?

在尋找解決辦法以前,我們先來看看mysqldump的備份選項--single-transaction--master-data[=value]的做用和使用限制。

  • --single-transaction

此選項將事務隔離模式設置爲REPEATABLE READ,並在備份數據以前向server發送START TRANSACTION SQL語句以顯示開啓一個事務快照。僅適用於InnoDB這樣的事務表,因爲是在事務快照內進行備份,這樣可使得備份的數據與獲取事務快照時的數據是一致的,並且不會阻塞任何應用程序對server的訪問。

在進行單事務備份時,爲確保有效的備份文件(正確的表內容和二進制日誌位置),不能有其餘鏈接應使用語句:ALTER TABLE,CREATE TABLE,DROP TABLE,RENAME TABLE,TRUNCATE等DDL語句。這會致使一致狀態被破壞,可能致使mysqldump執行SELECT檢索表數據時查詢到不正確的內容或備份失敗 。

注意:該選項僅適用於事務引擎表,對於MyISAM或MEMORY表因爲不支持事務,因此備份過程當中這些引擎表的數據仍可能發生更改。

  • --master-data[=value]

使用此選項備份時會在備份文件中生成change master to語句,使用的binlog pos是使用的備份server本身的binlog pos,可以使用備份文件用於將另外一臺服務器(恢復這個備份文件的服務器)設置爲備份server的從庫。

--dump-slave選項相似,若是選項值爲2,則CHANGE MASTER TO語句將做爲SQL註釋寫入備份文件,所以僅供參考;當備份文件被從新加載時,這個註釋不起做用。若是選項值爲1,則該語句不會註釋,並在從新加載備份文件時會生效(被執行)。若是未指定選項值,則默認值爲1。

指定此選項的用戶須要RELOAD權限,而且server必須啓用二進制日誌,由於這個位置是使用show master status獲取的(若是沒有開啓log_bin參數,則show master status輸出信息爲空),而不是使用show slave status獲取的。

--master-data選項自動關閉--lock-tables選項。同時還會打開--lock-all-tables,除非指定了--single-transaction選項,在指定了--single-transaction選項以後,只有在備份開始時間內才加全局讀取鎖。

so,--single-transaction選項中明確說明了若是使用了該選項,那麼在備份期間若是發生DDL,則可能致使備份數據一致性被破壞,select檢索不到正確的內容。另外,該選項僅僅只適用於事務引擎表,不適用於非事務引擎。做爲DBA,不少時候是很是無奈的,雖然有各類規範,可是保不齊就是有lo漏網之魚,這個時候,生活還得繼續,工做還得作好, 那麼,有什麼辦法能夠緩解這個問題嗎?有的:

1) 就如同上文中演示步驟中那樣,去掉--single-transaction選項進行備份,此時單獨使用--master-data選項時會自動開啓--lock-all-tables,備份過程當中整個實例全程鎖表,不會發生備份數據與獲取的binlog pos點不一致的問題,這樣,用該備份來搭建備庫時就不會出現數據衝突。可是問題顯而易見,備份期間數據庫不可用,若是採用這種方法,至少須要在業務低峯期進行備份
2) 使用innobackupex備份工具

二、如今看innobackupex

2.1. innobackupex備份過程解讀

  • A庫清空查詢日誌
[root@localhost ~]# echo > /home/mysql/data/mysqldata1/mydata/localhost.log 
  • 爲了更清晰地追蹤innobackupex是如何拷貝redo log的,咱們在A庫新開一個ssh會話2,使用以下腳本持續對錶t_luoxiaobo進行插入操做(該表爲innodb表),限於篇幅,請到以下爲知筆記連接獲取

  • http://5d096a11.wiz03.com/share/s/1t2mEh0a-kl_2c2NZ33kSiac1Rgvxq1vgkhL21ibWU2cLidk

  • A庫使用innobackupex執行備份,使用strace命令抓取備份過程當中的調用棧

[root@5f1772e3-0c7a-4537-97f9-9b57cf6a04c2 ~]# rm -rf /data/backup/full/* '# 注意:strace必須加-f選項,不然fork線程的調用棧打印不出來,由於innobackupex備份時是單進程多線程的的方式執行備份的' [root@5f1772e3-0c7a-4537-97f9-9b57cf6a04c2 ~]# strace -f innobackupex --defaults-file=/home/mysql/conf/my1.cnf --user=admin --password=letsg0\ --no-timestamp /data/backup/full 2> a.txt [root@5f1772e3-0c7a-4537-97f9-9b57cf6a04c2 ~]# 
  • 查看general_log日誌中的記錄(刪掉了加壓腳本中的語句)
[root@5f1772e3-0c7a-4537-97f9-9b57cf6a04c2 ~]# cat /home/mysql/data/mysqldata1/mydata/localhost.log ...... 2017-07-04T09:58:21.037311Z 6842 Query set autocommit=1 2017-07-04T09:58:21.037916Z 6842 Query SET SESSION wait_timeout=2147483 2017-07-04T09:58:21.038551Z 6842 Query SELECT CONCAT(@@hostname, @@port) 2017-07-04T09:58:21.043617Z 6843 Query SET SESSION wait_timeout=2147483 2017-07-04T09:58:21.043870Z 6843 Query SHOW VARIABLES 2017-07-04T09:58:21.050047Z 6843 Query SHOW ENGINE INNODB STATUS 2017-07-04T09:58:34.949224Z 6843 Query SET SESSION lock_wait_timeout=31536000 2017-07-04T09:58:34.949820Z 6843 Query FLUSH NO_WRITE_TO_BINLOG TABLES 2017-07-04T09:58:34.950331Z 6843 Query FLUSH TABLES WITH READ LOCK 2017-07-04T09:58:38.117764Z 6843 Query SHOW MASTER STATUS 2017-07-04T09:58:38.118012Z 6843 Query SHOW VARIABLES 2017-07-04T09:58:38.123193Z 6843 Query FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS 2017-07-04T09:58:38.327210Z 6843 Query UNLOCK TABLES 2017-07-04T09:58:38.329734Z 6843 Query SELECT UUID() 2017-07-04T09:58:38.330003Z 6843 Query SELECT VERSION() 2017-07-04T09:58:38.539340Z 6843 Quit ...... 
  • 從上面的記錄中能夠看到,與mysqldump相比,innobackupex備份時對數據庫的操做多了一個FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS,稍後對這句的做用進行解釋

  • 由於innobackupex是物理拷貝文件,數據並不像mysqldump那樣經過對數據庫表執行select語句查詢進行備份,而是經過拷貝磁盤文件進行備份的,因此,主體的備份流程還須要看strace的調用棧,限於篇幅緣由,詳見爲知筆記外鏈:http://5d096a11.wiz03.com/share/s/1t2mEh0a-kl_2c2NZ33kSiac2ZRJlK3qIAQr2LjYMx2xMkCD

  • 經過備份輸出日誌和strace調用棧,整理的流程圖以下(全備)

 

2.2. innobackupex爲何須要這麼作

  • innobackupex備份時啓動一個進程多個線程,經過拷貝磁盤文件實現物理備份,爲了保證備份數據的一致性,須要在備份過程當中恰當的時機發送一些加鎖解鎖語句與數據庫實例進行交互,so...要了解innobackupex工具的整個備份過程當中作了哪些事情,咱們就須要查看general_log和備份過程當中的日誌輸出(其實strace調用棧信息裏就能夠了解到innobackupex所作的全部事情,可是。。都是系統調用,看起來比較費勁),對於備份過程當中的日誌輸出,這裏就再也不熬述,詳見上文中的"全備流程圖",本小節咱們只介紹general_log中的輸出重點語句,以下:
  • FLUSH NO_WRITE_TO_BINLOG TABLES、FLUSH TABLES WITH READ LOCK、SHOW MASTER STATUS、UNLOCK TABLES幾個語句的做用與mysqldump備份過程當中的這幾個語句的做用同樣
  • FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS,該語句在mysqldump備份過程當中沒有 * 這句的做用是在全部的事務表和非事務表備份完成,獲取了全局讀鎖,且使用SHOW MASTER STATUS語句獲取了binlog pos以後,執行刷新redo log buffer中的日誌到磁盤中,而後redo log copy線程拷貝這最後的redo log日誌數據(爲何說是最後的redo log日誌數據呢?由於此時使用FLUSH TABLES WITH READ LOCK加鎖以後,使用UNLOCK TABLES釋放全局讀鎖以前,不會再有新的請求進來,),拷貝完成以後就中止copy線程並關閉xtrabackup_logfile文件。而後再使用UNLOCK TABLES釋放全局讀鎖。 * 詳見姜承堯老師的推文:http://chuansong.me/n/372118651979

2.3. innobackupex有什麼坑嗎?

  • 從上文中介紹的innobackupex的備份流程和原理上,咱們能夠得知,innobackupex工具有份過程當中是不會出現前面提到的mysqldump備份工具的"坑一"的。由於innobackupex備份工具是在全部事務表和非事務表都備份完成以後纔會執行UNLOCK TABLES釋放全局讀鎖,so...從加鎖以後,解鎖以前不可能有任何其餘的DML請求可以對數據作修改,從而保證的備份數據的一致性。

  • 那麼,mysqldump的"坑二"呢?咱們來看下面請看演示過程

  • A庫使用以下腳本持續對錶t_luoxiaobo進行插入操做(該表爲innodb表),限於篇幅,請到以下爲知筆記連接獲取(留意把program_name變量值改成"innobackupex")

  • http://5d096a11.wiz03.com/share/s/1t2mEh0a-kl_2c2NZ33kSiac1Rgvxq1vgkhL21ibWU2cLidk

  • A庫新開一個ssh會話2,執行innobackupex備份,留意日誌打印過程。從下面的結果中,咱們能夠看到報錯終止了

[root@localhost ~]# innobackupex --defaults-file=/home/mysql/conf/my1.cnf --user=admin --password=letsg0 --no-timestamp /data/backup/full ...... 170705 14:47:21 >> log scanned up to (129385440) 170705 14:47:21 [01] ...done 170705 14:47:21 [01] Copying ./luoxiaobo/t_luoxiaobo.ibd to /data/backup/full/luoxiaobo/t_luoxiaobo.ibd #從這裏能夠看到,正在備份t_luoxiaobo表 170705 14:47:22 >> log scanned up to (129385507) '#這裏能夠看到,scanned lsn的時候,發現了DDL作的修改,報錯了,告訴你DDL已經執行,將沒法保證備份數據的一致性' InnoDB: Last flushed lsn: 129385032 load_index lsn 129385558 [FATAL] InnoDB: An optimized(without redo logging) DDLoperation has been performed. All modified pages may not have been flushed to the disk yet. PXB will not be able take a consistent backup. Retry the backup operation 2017-07-05 14:47:23 0x7f9125365700 InnoDB: Assertion failure in thread 140261371303680 in file ut0ut.cc line 916 InnoDB: We intentionally generate a memory trap. InnoDB: Submit a detailed bug report to http://bugs.mysql.com. InnoDB: If you get repeated assertion failures or crashes, even InnoDB: immediately after the mysqld startup, there may be InnoDB: corruption in the InnoDB tablespace. Please refer to InnoDB: http://dev.mysql.com/doc/refman/5.7/en/forcing-innodb-recovery.html InnoDB: about forcing recovery. 06:47:23 UTC - xtrabackup got signal 6 ; This could be because you hit a bug or data is corrupted. This error can also be caused by malfunctioning hardware. Attempting to collect some information that could help diagnose the problem. As this is a crash and something is definitely wrong, the information collection process might fail. Thread pointer: 0x0 Attempting backtrace. You can use the following information to find out where mysqld died. If you see no messages after this, something went terribly wrong.. ...... 
  • 發生什麼了?

  • 首先,咱們知道,innobackupex在備份事務表時,是沒有對數據庫加鎖的,so..這個時候,其實DDL是容許執行的,innobackupex持續在備份innodb事務表期間,若是被執行DDL的表是在innobackupex備份完成以後發起,那麼在下一次scan lsn的時候innobackupex將發現DDL更改,報錯終止,若是是在備份非事務表期間發起的DDL,那麼將被FLUSH TABLE WITH READ LOCK語句阻塞。因此,對於使用innobackupex備份的生產環境,要執行DDL語句,也須要避開備份時間

  • 那麼,除了這個,還有其餘坑嗎?

  • 前面在介紹mysqldump備份過程當中的FLUSH TABLES和FLUSH TABLES WITH READ LOCK語句的時候,提到過三個注意事項,innobackupex備份過程當中爲了得到一個一致性備份,仍然會使用這兩個語句對數據庫進行刷新表緩存、加全局讀鎖,也就是說,mysqldump使用這兩個語句可能會踩到的坑,在innobackupex中也會碰到,以下: * 1)、若是一個會話中使用LOCK TABLES語句對某表加了表鎖,在該表鎖未釋放前,那麼另一個會話若是執行FLUSH TABLES和FLUSH TABLES WITH READ LOCK語句會被阻塞,而若是數據庫中lock_wait_timeout參數設置時間過短,innobackupex將會由於執行FLUSH TABLES WITH READ LOCK語句獲取全局讀鎖超時而致使備份失敗退出 * 2)、若是一個會話正在執行DDL語句,那麼另一個會話若是執行FLUSH TABLES和FLUSH TABLES WITH READ LOCK語句會被阻塞,而若是數據庫中lock_wait_timeout參數設置時間過短,innobackupex將會由於執行FLUSH TABLES WITH READ LOCK語句獲取全局讀鎖超時而致使備份失敗退出 * 3)、若是一個會話正在執行DML大事務(DML語句正在執行,數據正在發生修改,而不是使用lock in share mode和for update語句來顯式加鎖),那麼另一個會話若是執行FLUSH TABLES和FLUSH TABLES WITH READ LOCK語句會被阻塞,而若是數據庫中lock_wait_timeout參數設置時間過短,innobackupex將會由於執行FLUSH TABLES WITH READ LOCK語句獲取全局讀鎖超時而致使備份失敗退出

  • 可是,細心的童鞋可能已經發現了,innobackupex備份時的general_log中執行FLUSH NO_WRITE_TO_BINLOG TABLES語句以前,有這樣一句語句:SET SESSION lock_wait_timeout=31536000,備份時會在session級別把鎖超時時間改了,so...除了加表鎖忘記釋放以外,其餘兩種狀況估計不太可能碰到鎖超時的狀況!!

  • 固然,若是天天備份一次,那麼咱們不太可能讓innobackupex在備份時,獲取全局讀鎖時等待31536000秒,so。。咱們可使用innobackupex的選項--kill-long-queries-timeout,來再獲取全局讀鎖時,若是某查詢阻塞了得到該FLUSH TABLE WITH READ LOCK語句時間超過這個閥值,那麼就對該會話執行kill,殺掉這個鏈接,固然,你也許會說對數據作修改的不能殺,只能殺查詢的,那麼咱們可使用--kill-long-query-type=all|select選項。下面列出這倆選項的含義:

  • --kill-long-query-type=all|select * 該選項指定哪些類型的查詢在指定的查詢時間以後尚未執行完成時被kill掉,以釋放阻塞加全局讀鎖的鎖,默認值爲all,有效值有:all和select * 執行該選項須要有process和super權限

  • --kill-long-queries-timeout=SECONDS * 該選項指定innobackupex在執行FLUSH TABLES WITH READ LOCK時碰到阻塞其得到鎖的查詢時,等待該參數指定的秒數以後,若是仍然有查詢在運行,則執行kill掉這些查詢 * 默認值爲0,表示innobackupex 不啓用嘗試kill掉任何查詢的功能

  • PS:

  • 不少人喜歡在備份前先flush binary logs一把,其實在有大事務對數據進行修改時,一不當心可能就會出現數據庫hang死,因此不建議這麼作

  • innobackupex備份期間,在數據庫中建立的鏈接不要誤殺,不然備份失敗

三、總結

相關文章
相關標籤/搜索