從RDS(兼容MySQL) 導出數據案例分析
作者:丁奇
背景
MySQL全量邏輯備份恢複最基礎的方法,就是mysqldump生成文本,再通過source 命令直接導入。一般用於實例遷移或者版本升級。
這裏說明最近碰到的一個失敗例子。
描述
這個例子可以簡要複現如下,在源庫上執行如下操作:
use mydb;
create table t1 (id int);
create view v1 as select * from t1;
drop table t1;
之後執行 mysqldump mydb,發現mysqldump中途退出。簡化後出錯原因很明顯,就是視圖v1對應的表t1已經不存在,這個視圖本身非法。
這個錯誤很危險,因為如果沒有捕獲這個錯誤,直接認為mysqldump執行完成,並將生成的結果應用於目標庫,則會導致數據丟失!
其實這個問題並不像看起來那麼簡單。
一個問題
mysqldump隻需要生成show create view v1的結果即可—-這會導致在目標庫source的時候報錯—-為什麼會在dump的時候就報錯?
MySQL Tips: mysqldump導出整庫,默認情況下,會在導出前對整庫裏麵的每個表加讀鎖,即Lock tables tb1 read, tb2 read,…, tbn read.
本例中由於視圖v1需要的實體表t1已經不存在,lock table v1 read 返回失敗。因此整個庫所有表都沒有正常導出。如果以此輸出結果導入到目標庫,則整庫數據丟失。
MySQL Tips: mysqldump若正常結束,生成的文件結果最後一行是“– Dump completed on + 時間”。
使用dump的結果文件必須先檢查結果文件的完整性,判斷末行的文本是一個推薦操作。
導整庫的時候為什麼需要對所有表都加讀鎖呢?目的是為了得到一致性讀視圖。也就是說,在導出整庫期間,不會有更新,這樣才保證導出數據的一致性。
而實際上這個是曆史遺留問題,InnoDB本身支持一致性讀,也就是說隻要啟動一個事務(begin/start transaction),則在此事務存續期間,對於本實例內InnoDB表的更新,此導出事務均不可見。也就是說InnoDB事務可以保證事務期間看到的視圖與事務啟動瞬間看到的一致。
而鎖表會堵住更新,導庫又往往是長時間的操作,因此備份期間能允許讀寫對OLTP應用來說至關重要。
MySQL Tips: 若確保mysqldump導出的數據中隻有InnoDB表,可以用 –single-transaction 避免鎖表。
問題繼續
加了–single-transaction後不需要鎖表,是否還會導致mysqldump失敗?答案依然是會。這次我們發現輸出的錯誤是 “SHOW FIELDS FROM v1″ 失敗。由於show fields v1需要解析視圖並列出執行結果的列信息,而表t1已經不存在因此報錯。
問題是,為什麼需要執行一個 SHOW FIELDS?
這就說到restore的依賴問題。
MySQL Tips: mysqldump生成導出文件時。同一個庫內實體表先生成,之後是視圖。多個實體表是按照字母升序生成,多個視圖也是按字母升序生成。
這樣就可能存在這樣的情況,某個視圖v1依賴視圖v2,而v1的字母排序前於v2。比如視圖名為x,而依賴的另一個視圖名為y。這樣在按順序輸出的時候,如果不做任何處理,在輸出文件中就會先出現create view x …而由於x依賴於y但y還沒有生成,就會導致restore階段執行失敗。
MySQL Tips: mysqldump解決視圖依賴問題的方法,就是在生成實體表階段,如果碰到視圖,則創建一個同名的臨時實體表,該表的結果與視圖完全相同。
配合的策略是在生成真正的視圖前,先將臨時實體表刪除。這樣在restore階段,創建任何視圖V1前,它所依賴的視圖V2有兩種情況:
1) 字母排序V2 > V1,則當前庫中有一個名為V2的臨時實體表,這樣視圖V1能夠正常創建;
2) 字母排序V2 < V1,則此時視圖V2已經存在,這樣視圖V1能夠正常創建;
以上策略解決了視圖循環依賴的問題,這個機製需要早生成實體表階段得到視圖執行結果的列名,因此需要執行一個 SHOW FIELDS。
實踐建議
上麵的分析說明了”為什麼”,以下操作型的讀者希望的實踐建議:
1) 為避免無效視圖影響導出,可以在調用mysqldump時增加–force參數,強行忽略此錯誤。這個忽略不會對數據造成影響;
2) 使用–result-file參數保存mysqldump結果,同時將所有控製台收到的返回都作為報警返回給調用端;
3) 檢查 result-file的最後一行,若無“Dump completed on”字樣,則返回嚴重錯誤,終止備份恢複流程。
最後更新:2017-04-03 08:26:24