在分散式存儲系統中,系統可用性是最重要的指標之一,需要保證在機器發生故障時,系統可用性不受影響。本文主要介紹數據備份的方式,以及如何保證多個數據副本的一致性,在系統出現機器或網路故障時,如何保持系統的高可用性。 ...
在分散式存儲系統中,系統可用性是最重要的指標之一,需要保證在機器發生故障時,系統可用性不受影響,為了做到這點,數據就需要保存多個副本,並且多個副本要分佈在不同的機器上,只要多個副本的數據是一致的,在機器故障引起某些副本失效時,其它副本仍然能提供服務。本文主要介紹數據備份的方式,以及如何保證多個數據副本的一致性,在系統出現機器或網路故障時,如何保持系統的高可用性。
數據備份
數據備份是指存儲數據的多個副本,備份方式可以分為熱備和冷備,熱備是指直接提供服務的備副本,或者在主副本失效時能立即提供服務的備副本,冷備是用於恢複數據的副本,一般通過Dump的方式生成。
數據熱備按副本的分佈方式可分為同構系統和非同步系統。同構系統是把存儲節點分成若幹組,每組節點存儲相同的數據,其中一個主節點,其他為備節點;異構系統是把數據劃分成很多分片,每個分片的多個副本分佈在不同的存儲節點,存儲節點之間是異構的,即每個節點存儲的數據分片集合都不相同。在同構系統中,只有主節點提供寫服務,備節點只提供讀服務,每個主節點的備節點數可以不一樣,這樣在部署上會有更大的靈活性。在異構系統中,所有節點都是可以提供寫服務的,並且在某個節點發生故障時,會有多個節點參與故障節點的數據恢復,但這種方式需要比較多的元數據來確定各個分片的主副本所在的節點,數據同步機制也會比較複雜。相比較而言,異構系統能提供更好的寫性能,但實現比較複雜,而同構系統架構更簡單,部署上也更靈活。鑒於互聯網大部分業務場景具有寫少讀多的特性,我們選擇了更易於實現的同構系統的設計。
系統數據備份的架構如下圖所示,每個節點代表一臺物理機器,所有節點按數據分佈劃分為多個組,每一組的主備節點存儲相同的數據,只有主節點能提供寫服務,主節點負責把數據變更同步到所有的備節點,所有節點都能提供讀服務。主節點上會分佈全量的數據,所以主節點的數量決定了系統能存儲的數據量,在系統容量不足時,就需要擴容主節點數量。在系統的處理能力上,如果是寫能力不足,只能通過擴容主節點數來解決;而在寫能力不足時,則可以通過增加備節點來提升。每個主節點擁有的備節點數量可以不一樣,這在各個節點的數據熱度不一樣時特別有用,可以通過給比較熱的節點增加更多的備節點實現用更少的資源來提升系統的處理能力。
同步機制
在上面的備份架構中,每個分組只有主節點接收寫請求,然後由主節點負責把數據同步到所有的備節點,如下圖所示,主節點採用一對多的方式進行同步,相對於級聯的方式,這種方式在某個備節點故障時,不會影響其它備節點的同步。在CAP理論中,可用性和一致性是一對矛盾體,在這裡主節點執行寫操作後會立即回覆客戶端,然後再非同步同步數據到備節點,這樣並不能保證主備節點的數據強一致性,主備數據會有短暫的不一致,通過犧牲一定的一致性來保證系統的可用性。在這種機制下,客戶端可能在備節點讀到老數據,如果業務要求數據強一致性,則可以在讀請求中設置只讀主選項,這樣讀請求就會被介面層轉發到主節點,這種情況下備節點只用於容災,不提供服務。
為了保證主備節點的數據一致性,需要一種高效可靠的數據同步機制。同步分為增量同步和全量同步,增量同步是主節點把寫請求直接轉發到備節點執行,全量同步是主節點把本地的數據發到備節點進行覆蓋。接下來詳細介紹同步機制的實現,同步的整體流程如下圖所示。
系統中數據分片的單位是一致性哈希環中的VNode(虛擬節點),每個VNode有一個自增的同步序列號SyncSeq,VNode中所包含的數據的每一個寫操作都會觸發它的SyncSeq進行自增,這樣在每個VNode內SyncSeq就標識了每一次寫操作,並且SyncSeq的大小也反映了寫操作的執行順序。數據的每次寫操作除了修改數據,還會保存寫操作對應的SyncSeq,後面可以看到,SyncSeq是同步機制可靠性的基礎。
主節點的寫進程收到寫請求後,先修改數據,把當前VNode的SyncSeq加1並更新到數據中。接下來會記錄Binlog,Binlog是一個三元組<VNode, SyncSeq, Key>,可以唯一標識整個系統的一次寫操作。Binlog會寫入到Binlog隊列和Binlog緩存,Binlog隊列由其他進程合併寫入到Binlog文件,Binlog緩存是一個可淘汰的哈希表,用於快速查找。然後把寫請求緩存在一個可淘汰的哈希表中,寫請求用於進行增量同步,哈希表存儲了三元組<VNode, SyncSeq, Req>,這裡緩存的寫請求包大小有限制,超過限制的寫請求不進行緩存。 最後寫進程會更新同步進度表,如下圖所示,同步進度表記錄了各個VNode主節點同步到各個備節點的進度,主節點的SyncSeq是各個VNode最後一次寫操作的SyncSeq,備節點的SyncSeq是已同步的最大的SyncSeq。
主備節點的數據同步由主節點上的同步進程非同步進行,通過掃描上圖的同步進度表中主備節點的SyncSeq差異就可知備節點需要同步哪些數據。同步進程通過同步進度表確定需要同步的二元組<VNode, SyncSeq>,先去寫請求緩存中查找,如果找到,則把寫請求發給備節點進行增量同步。如果在寫請求緩存中未找到,則依次去Binlog緩存和Binlog文件中查找對應的Binlog,通過Binlog對數據進行全量同步。最後再更新同步進度表中已同步的備節點SyncSeq,至此一個完整的數據同步流程已經完成。
接下來介紹一下同步協議如何保證同步的高效和可靠。為了讓同步包嚴格按照主節點的發送順序到達備節點,採用TCP協議進行同步,在主節點的每個VNode上到每一個備節點建立一個TCP連接,記為一個同步連接。在每一個同步連接上,主節點會一次性批量發送多個同步包,備節點也會記錄已同步的SyncSeq,對每一個同步包會檢查攜帶的SyncSeq是否符合預期,如果符合預期,則執行同步寫操作,執行成功是更新已同步的SyncSeq,在這種情況寫備節點也不需要回應主節點,主節點在未收到備節點的回應時,會認為同步一切正常。只有以下異常情況下,備節點才會回應主節點:
- 在正常同步後第一次收到錯誤的SyncSeq,回應主節點自己所期望的SyncSeq,主節點收到回應後,會從備節點所期望的SyncSeq開始同步,需要註意的是,備節點在連續收到錯誤SyncSeq時,只需對第一個錯誤回應,否則主節點會出現重覆同步的情況;
- 同步連接在斷連後重新連接時,備節點告知主節點自己所期望開始同步的SyncSeq,主節點從該SyncSeq開始同步;
- SyncSeq符合期望但執行出錯,一般是增量同步才可能出現,備節點回應主節點同步出錯,主節點收到回應後,把出錯的同步包改為全量同步。
在增量同步和全量同步交叉進行的情況下,如果某次全量同步已同步了最新的數據,後續的增量同步可能導致寫操作重覆執行,為了避免這種情況,備節點會校驗同步包中的SyncSeq和數據中的SyncSeq,如果前者不大於後者,說明數據已執行了這次寫操作,直接跳過不執行,也不需要回應主節點,這就是為什麼需要在數據中保存SyncSeq的原因。
通過上面介紹和分析,可以看出採用同步連接、批量同步的方法,正常情況下只有單向的同步流量,是非常高效的;而在異常情況下,通過出錯回應、SyncSeq校驗等機制,保證了同步的可靠性。
容災機制
如果系統需要具有容災能力,即在機器發生故障時,系統的可用性基本不受影響,那麼系統中所有數據至少需要有兩個以上的副本,並且系統的處理能力要有一定的冗餘,需要保證在故障機器不能提供服務時,系統不會過載。一般來說,數據的副本數量越多,系統的處理能力越冗餘,系統的容災能力越強。更進一步,還需要考慮物理部署,通過把數據的不同副本分佈在不同機架、不同機房、甚至是不同城市,來把系統的容災能力提升到不同的級別。
配置運維中心會監控系統存儲層所有節點的狀態,存儲節點會定時上報心跳,如果配置運維中心在一段時間未收到某個存儲節點的心跳,則把該節點的狀態標記為故障,併進行故障處理流程。首先需要禁止故障節點繼續提供服務,即通知介面層不再把客戶端請求轉發的故障節點,如果故障節點是主節點,配置運維中心會查詢並對比所有備節點的同步進度,選擇數據最新的備節點,將其切換為主節點。由於所有備節點也會記錄Binlog,所以在切換為主節點之後,可以直接向其它備節點進行同步。這裡的主備切換可能會導致少量的數據丟失,如果業務不能容忍這樣的數據丟失,則需要使用其它強一致性的方案。
在容災切換之後,還需要進行故障節點的恢復,以便系統恢復到正常的狀態。故障機器恢復後,就會進入死機恢復流程,無論故障節點在故障前是主節點還是備節點,故障恢復後的角色都是備節點。首先待恢復節點需要把機器上所有的數據清空;接著主節點會把當前所有VNode的SyncSeq複製到待恢復節點,並且全量複製所有數據;在全量複製完成之後,開始進行數據同步,由前面的同步機制可知,同步的SyncSeq會從之前複製到待恢復節點的狀態開始追趕;在主節點和待恢復節點之間的SyncSeq差異縮小到正常範圍時,待恢復節點的角色就變為備節點,開始提供服務。
配置運維中心會監控主備節點之間的SyncSeq差異,如果某個備節點差異達到一定的閾值,則禁止該備節點提供服務,如果差異在比較長的時間之後仍然無法恢復,則會觸發死機恢復流程。
數據回檔
最後再簡單介紹下數據冷備和回檔,主要是由備份系統負責。備份任務一般是手動或定時發起,屬於業務級別的,備份系統收到一個業務的備份任務後,會遠程備份業務的所有數據,過程比較簡單,就是遍歷所有的存儲節點,把屬於該業務的所有數據寫入到遠程文件系統中,每次備份都需要記錄開始時間和結束時間,作為數據回檔的基準。
系統中所有的寫操作都會記錄一份遠程的流水,每條流水都記錄了寫操作的時間戳,由流水中心統一存儲。結合數據冷備和流水,可以恢復到冷備完成後任意時刻的數據。備份系統收到一個業務回檔任務後,首先停止該業務的服務,然後清空業務的所有數據,接著從冷備做一次全量的恢復,然後再重放流水到指定時間點,即可完成數據回檔。需要註意的是這裡的冷備並不是快照,在進行冷備的時候,寫操作也正常執行,所以從冷備開始時間重放流水會導致很多的寫操作重覆執行,這裡通過數據版本校驗來避免這個問題,在數據中保存了版本信息,在寫操作流水中也記錄了對應的寫操作完成後的數據版本,重放流水的時候,如果流水中記錄的版本不比數據中的版本新,則直接跳過這條流水,這樣就保證了數據回檔的準確性。