在分佈式存儲系統中,系統可用性是最重要的指標之一,須要保證在機器發生故障時,系統可用性不受影響,爲了作到這點,數據就須要保存多個副本,而且多個副本要分佈在不一樣的機器上,只要多個副本的數據是一致的,在機器故障引發某些副本失效時,其它副本仍然能提供服務。本文主要介紹數據備份的方式,以及如何保證多個數據副本的一致性,在系統出現機器或網絡故障時,如何保持系統的高可用性。緩存
數據備份是指存儲數據的多個副本,備份方式能夠分爲熱備和冷備,熱備是指直接提供服務的備副本,或者在主副本失效時能當即提供服務的備副本,冷備是用於恢復數據的副本,通常經過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。spa
主備節點的數據同步由主節點上的同步進程異步進行,經過掃描上圖的同步進度表中主備節點的SyncSeq差別就可知備節點須要同步哪些數據。同步進程經過同步進度表肯定須要同步的二元組<VNode, SyncSeq>,先去寫請求緩存中查找,若是找到,則把寫請求發給備節點進行增量同步。若是在寫請求緩存中未找到,則依次去Binlog緩存和Binlog文件中查找對應的Binlog,經過Binlog對數據進行全量同步。最後再更新同步進度表中已同步的備節點SyncSeq,至此一個完整的數據同步流程已經完成。設計
接下來介紹一下同步協議如何保證同步的高效和可靠。爲了讓同步包嚴格按照主節點的發送順序到達備節點,採用TCP協議進行同步,在主節點的每一個VNode上到每個備節點創建一個TCP鏈接,記爲一個同步鏈接。在每個同步鏈接上,主節點會一次性批量發送多個同步包,備節點也會記錄已同步的SyncSeq,對每個同步包會檢查攜帶的SyncSeq是否符合預期,若是符合預期,則執行同步寫操做,執行成功是更新已同步的SyncSeq,在這種狀況寫備節點也不須要回應主節點,主節點在未收到備節點的迴應時,會認爲同步一切正常。只有如下異常狀況下,備節點纔會迴應主節點:blog
在增量同步和全量同步交叉進行的狀況下,若是某次全量同步已同步了最新的數據,後續的增量同步可能致使寫操做重複執行,爲了不這種狀況,備節點會校驗同步包中的SyncSeq和數據中的SyncSeq,若是前者不大於後者,說明數據已執行了此次寫操做,直接跳過不執行,也不須要回應主節點,這就是爲何須要在數據中保存SyncSeq的緣由。
經過上面介紹和分析,能夠看出採用同步鏈接、批量同步的方法,正常狀況下只有單向的同步流量,是很是高效的;而在異常狀況下,經過出錯迴應、SyncSeq校驗等機制,保證了同步的可靠性。
若是系統須要具備容災能力,即在機器發生故障時,系統的可用性基本不受影響,那麼系統中全部數據至少須要有兩個以上的副本,而且系統的處理能力要有必定的冗餘,須要保證在故障機器不能提供服務時,系統不會過載。通常來講,數據的副本數量越多,系統的處理能力越冗餘,系統的容災能力越強。更進一步,還須要考慮物理部署,經過把數據的不一樣副本分佈在不一樣機架、不一樣機房、甚至是不一樣城市,來把系統的容災能力提高到不一樣的級別。
配置運維中心會監控系統存儲層全部節點的狀態,存儲節點會定時上報心跳,若是配置運維中心在一段時間未收到某個存儲節點的心跳,則把該節點的狀態標記爲故障,並進行故障處理流程。首先須要禁止故障節點繼續提供服務,即通知接口層再也不把客戶端請求轉發的故障節點,若是故障節點是主節點,配置運維中心會查詢並對比全部備節點的同步進度,選擇數據最新的備節點,將其切換爲主節點。因爲全部備節點也會記錄Binlog,因此在切換爲主節點以後,能夠直接向其它備節點進行同步。這裏的主備切換可能會致使少許的數據丟失,若是業務不能容忍這樣的數據丟失,則須要使用其它強一致性的方案。
在容災切換以後,還須要進行故障節點的恢復,以便系統恢復到正常的狀態。故障機器恢復後,就會進入死機恢復流程,不管故障節點在故障前是主節點仍是備節點,故障恢復後的角色都是備節點。首先待恢復節點須要把機器上全部的數據清空;接着主節點會把當前全部VNode的SyncSeq複製到待恢復節點,而且全量複製全部數據;在全量複製完成以後,開始進行數據同步,由前面的同步機制可知,同步的SyncSeq會從以前複製到待恢復節點的狀態開始追趕;在主節點和待恢復節點之間的SyncSeq差別縮小到正常範圍時,待恢復節點的角色就變爲備節點,開始提供服務。
配置運維中心會監控主備節點之間的SyncSeq差別,若是某個備節點差別達到必定的閾值,則禁止該備節點提供服務,若是差別在比較長的時間以後仍然沒法恢復,則會觸發死機恢復流程。
最後再簡單介紹下數據冷備和回檔,主要是由備份系統負責。備份任務通常是手動或定時發起,屬於業務級別的,備份系統收到一個業務的備份任務後,會遠程備份業務的全部數據,過程比較簡單,就是遍歷全部的存儲節點,把屬於該業務的全部數據寫入到遠程文件系統中,每次備份都須要記錄開始時間和結束時間,做爲數據回檔的基準。
系統中全部的寫操做都會記錄一份遠程的流水,每條流水都記錄了寫操做的時間戳,由流水中心統一存儲。結合數據冷備和流水,能夠恢復到冷備完成後任意時刻的數據。備份系統收到一個業務回檔任務後,首先中止該業務的服務,而後清空業務的全部數據,接着從冷備作一次全量的恢復,而後再重放流水到指定時間點,便可完成數據回檔。須要注意的是這裏的冷備並非快照,在進行冷備的時候,寫操做也正常執行,因此從冷備開始時間重放流水會致使不少的寫操做重複執行,這裏經過數據版本校驗來避免這個問題,在數據中保存了版本信息,在寫操做流水中也記錄了對應的寫操做完成後的數據版本,重放流水的時候,若是流水中記錄的版本不比數據中的版本新,則直接跳過這條流水,這樣就保證了數據回檔的準確性。