MySQL備份的持續驗證:還原備份
非死book的MySQL數據庫遍布在我們位于全球的數據中心內,我們必須能在任何時間內從任何位置發生的故障中恢復。在發生此類災難事件后,不僅需要盡量快速可靠地恢復服務,而且需要確保整個過程不會丟失任何數據。為此我們構建了一套能夠對從備份中恢復數據庫的能力進行持續不斷測試的系統。
我們的還原系統包含兩個重要組件:
-
持續還原層(Continuous Restore Tier,CRT) - 負責對所有還原操作進行調度和監控。該組件會查找包含新備份的數據庫,為其創建還原作業,監控還原過程,并確保每個備份可以成功還原。
-
ORC 還原協調器(ORC) - 由負責執行還原的工作進程(Peon)和負載均衡器(Warchief)組成。Warchief接收來自CRT的新建還原作業并將其分配給Peon。Peon承載了負責執行實際還原過程的本地MySQL實例。
數據CRT會收集每個還原作業的進度信息,借此幫助我們了解數據庫還原操作的資源需求,ORC則可以幫助我們驗證備份的完整性。本文將側重于介紹ORC的內部原理,尤其是內部的Peon狀態機(State machine),以及在對單一數據庫進行還原的過程中我們遇到并克服的挑戰。
備份概述
在構建持續還原流程前,我們首先需要了解各種可用備份選項的本質。目前我們主要進行三種備份,所有備份內容均存儲在HDFS中:
- 完整邏輯備份 ,使用 mysqldump 每幾天進行一次。
- 差異(Diff)備份 在所有未進行完整備份的日子里進行。備份過程中會再次創建完整轉儲,但只存儲與上一次完整備份相比有差異的內容。我們會通過元數據記錄每次差異備份是基于哪個完整備份進行的。
- 二進制日志(Binlog)備份 從主數據庫持續不斷地流式傳輸至HDFS。
完整和差異備份均會將 --single-transaction 選項傳遞至 mysqldump ,這樣我們就可以對數據庫獲得一致的快照,該快照可取自從屬(Slave)或主(Master)實例。下文會將差異和完整備份統稱為轉儲(Dump)。
由于每天只進行一次轉儲操作,因此可通過Binlog備份確保自從備份之后,數據庫所執行的每一筆事務都能被我們記錄在案。隨后只要對轉儲內容執行還原操作將數據庫恢復至某個時點,隨后通過Binlog對事務進行重播(Replay),即可順利實現數據庫的時點還原。我們的所有數據庫服務器都使用了全局事務ID(GTID),因此在從Binlog備份進行事務重播時我們可以獲得額外的一層控制能力。
除了將備份存儲在HDFS中,我們還會將其寫入離場位置。Code as Craft活動中 Shlomo Priymak的講話 更詳細地介紹了我們的備份基礎架構。
ORC:ORC還原協調器
架構
ORC包含三個組件:
- Warchief - 負載均衡器。這是一種可暴露出Thrift接口的Python程序,通過接口可接收新的還原請求,并將其調度至可用的Peon。
- ORC DB - 負責維持分配給每個Peon的作業狀態、每個作業的當前狀態,以及Peon健康度狀態等信息的中央MySQL數據庫。Warchief會使用該數據庫中存儲的信息決定要將某個作業分配給哪個Peon,以及故障恢復過程中要使用的Peon。
- Peon - 負責還原操作的工作進程。Peon也使用Python編寫,可暴露出Thrift接口,通過該接口接收有關Peon的各類狀態信息。每個Peon會定期與ORC DB進行同步,查詢分配給自己的新作業,并匯報自己的健康度狀態。運行Peon的服務器上還運行了一個本地MySQL實例,備份將還原至該實例中。

內部原理:Peon
Peon中包含了從HDFS獲取備份,將其載入自己的本地MySQL實例,通過Binlog重播將實例推進至某一時點等操作的所有相關邏輯。Peon處理的每個還原作業會經歷下列五個步驟:
- 選擇(SELECT) - 決定需要用哪個備份進行還原(例如完整或增量,HDFS或離場等)。
- 下載(DOWNLOAD) - 將所選文件下載至磁盤。如果要還原的是完整備份,只需下載一個文件。對于差異備份,首先需要下載完整和差異備份,隨后在完整備份的基礎上應用差異備份,并將最終結果存儲到磁盤上。無論備份類型為何,至此我們已經在磁盤上有了一個 mysqldump 輸出的內容。
- 加載(LOAD) - 將下載的備份載入Peon的本地MySQL實例。按照與備份文件中每張表有關的語句進行解析,并行恢復每張表,這一過程類似于 Percona的Mydumper 。
- 驗證(VERIFY) - 針對載入MySQL的數據執行健康檢查(Sanity check)。
- 重播(REPLAY) - 如果有必要,在已還原備份的基礎上,下載二進制日志備份并進行事務重播。我們會使用 mysqlbinlog 程序篩選掉來自其他共置數據庫以及 空事務 的Binlog事件,隨后對同一個MySQL實例重播所需的事務。
每個步驟有自己對應的失敗狀態,因此如果某個作業在 DOWNLOAD 步驟失敗,將顯示為 DOWNLOAD_FAILED 狀態,并且不會繼續進入到 LOAD 步驟中。
Binlog的選擇邏輯
還原過程中最具挑戰的部分可能就是確定所要下載并重播的Binlog。完整和差異備份可以從主或從屬數據庫獲取,然而我們只從主數據庫創建Binlog備份。這意味著簡單的時間戳對比已經無法用于確定要重播的Binlog。
我們于 2014年 在所有服務器上部署了 GTID ,因此每筆事務都可以獲得一個全局唯一標識符。此外每臺運行MySQL的服務器也維持了一個 gtid_executed (GTID集)變量,可將其作為對應實例目前已執行事務數量的計數器。
在具備GTID的情況下,從主數據庫重播至從屬數據庫的事務可以維持自己的GTID,這也意味著我們可以清楚地知道每筆事務是否包含在某個GTID集中。此外還可以針對服務器的 gtid_executed 值進行超集/子集(Superset/subset)比較,因為該值實際上就是一種嚴格遞增的計數器和數學意義上的函數。
通過配合使用這些技術,即可在創建轉儲時記錄服務器的 gtid_executed 值,同時記錄每個Binlog文件中包含的GTID集,借此執行一致的比較并確定Binlog中的哪些事務需要重播。此外一旦需要重播的第一筆事務確定后,無需重新對比其他GTID即可確定需要重播的所有后續事務。我們還會使用 mysqlbinlog 的 --stop-datetime 選項確定Binlog流要在何處停止。
結論
面對災難性事件,備份是最后一重保障,備份的存在使得我們具備了更自信的故障恢復能力。然而僅僅創建備份是不夠的。ORC可以幫助我們對備份進行持續不斷的測試,借此驗證備份的完整性,并讓我們更清楚地確定成功還原數據所需提供的資源。
以非死book的規模,打造一個類似ORC這樣的系統還需要具備大量警報、監控,以及自動化的失敗檢測和補救機制。這些功能均是通過CRT實現的,我們將通過另一篇文章介紹如何將這種還原過程擴展至成千上萬個數據庫。
來自:http://www.infoq.com/cn/articles/continuous-validation-of-mysql-backup