ceph基於pglog的一致性協議

分佈式存儲系統一般採用多副本的方式來保證系統的可靠性,而多副本之間如何保證數據的一致性就是系統的核心。ceph號稱統一存儲,其核心RADOS既支持多副本,也支持糾刪碼。本文主要分析ceph的多副本一致性協議。緩存

1.pglog及讀寫流程

ceph使用pglog來保證多副本之間的一致性,pglog的示意圖以下:pglog主要是用來記錄作了什麼操做,好比修改,刪除等,而每一條記錄裏包含了對象信息,還有版本。
ceph使用版本控制的方式來標記一個PG內的每一次更新,每一個版本包括一個(epoch,version)來組成:其中epoch是osdmap的版本,每當有OSD狀態變化如增長刪除等時,epoch就遞增;version是PG內每次更新操做的版本號,遞增的,由PG內的Primary OSD進行分配的。
pglog-entry
每一個副本上都維護了pglog,pglog裏最重要的兩個指針就是last_complete和last_update,正常狀況下,每一個副本上這兩個指針都指向同一個位置,當出現機器重啓、網絡中斷等故障時,故障副本的這兩個指針就會有所區別,以便於來記錄副本間的差別。網絡

爲了便於說明ceph的一致性協議,先簡要描述一下ceph的讀寫處理流程。
寫處理流程:
1)client把寫請求發到Primary OSD上,Primary OSD上將寫請求序列化到一個事務中(在內存裏),而後構造一條pglog記錄,也序列化到這個事務中,而後將這個事務以directIO的方式異步寫入journal,同時Primary OSD把寫請求和pglog(pglog_entry是由primary生成)發送到Replicas上;
2)在Primary OSD將事務寫到journal上後,會經過一系列的線程和回調處理,而後將這個事務裏的數據寫入filesystem(只是寫到文件系統的緩存裏,會有線程按期刷數據),這個事務裏的pglog記錄(也包括pginfo的last_complete和last_update)會寫到leveldb,還有一些擴展屬性相關的也在這個事務裏,在遍歷這個事務時也會寫到leveldb;
3)在Replicas上,也是進行相似於Primary的動做,先寫journal,寫成功會給Primary發送一個committed ack,而後將這個事務裏的數據寫到filesystem,pglog與pginfo寫到leveldb裏,寫完後會給Primary發送另一個applied ack;
4)Primary在本身完成journal的寫入時,以及在收到Replica的committed ack時都會檢查是否多個副本都寫入journal成功了,若是是則向client端發送ack通知寫完成;Primary在本身完成事務寫到文件系統和leveldb後,以及在收到replica的applied ack時都會檢查是否多個副本都寫文件系統成功,若是是則向client端發送ack通知數據可讀;
對讀流程來講,就比較簡單,都是由Primary來處理,這裏就很少說了。app

2.故障恢復

ceph在進行故障恢復的時候會通過peering的過程,簡要來講,peering就是對比各個副本上的pglog,而後根據副本上pglog的差別來構造missing列表,而後在恢復階段就能夠根據missing列表來進行恢復了。peering是按照pg爲單位進行的,在進行peering的過程當中,I/O請求是會掛起的,當進行完peering階段進入recovery階段時,I/O能夠繼續進行,不過當I/O請求命中了missing列表的時候,對應的這個待恢復的對象會優先進行恢復,當這個對象恢復完成後,再進行I/O的處理。
由於pglog記錄數有限制,當對比各個副本上的pglog時,發現故障的副本已經落後太多了,這樣就沒法根據pglog來恢復了,因此這種狀況下就只能全量恢復,稱爲backfill,壞盤壞機器或者集羣擴容時也會觸發backfill,這裏不作介紹,後續單獨一篇文章來進行分析。異步

基於pglog的一致性協議包含兩種恢復過程,一個是Primary掛掉後又起來的恢復,一種是Replica掛掉後又起來的恢復。分佈式

2.1Primary故障恢復

recover-primary
簡單起見,圖中的數字就表示pglog裏不一樣對象的版本。
1)正常狀況下,都是由Primary處理client端的I/O請求,這時,Primary和Replicas上的last_update和last_complete都會指向pglog最新記錄;
2)當Primary掛掉後,會選出一個Replica做爲「臨時主」,這個「臨時主」負責處理新的讀寫請求,而且這個時候「臨時主」和剩下的Replicas上的last_complete和last_update都更新到該副本上的pglog的最新記錄;
3)當原來的Primary又重啓時,會從本地讀出pginfo和pglog,當發現last_complete<last_update時,last_complete和last_update之間就可能存在丟失的對象,遍歷last_complete到last_update之間的pglog記錄,對於每一條記錄,從本地讀出該記錄裏對象的屬性(包含本地持久化過的版本),對比pglog記錄裏的對象版本與讀出來的版本,若是讀出來的對象版本小於pglog記錄裏的版本,說明該對象不是最新的,須要進行恢復,所以將該對象加到missing列表裏
4)Primary發起peering過程,即「搶回原來的主」,選出權威日誌,通常就是「臨時主」的pglog,將該權威日誌獲取過來,與本身的pglog進行merge_log的步驟,構建出missing列表,而且更新本身的last_update爲最新的pglog記錄(與各個副本一致),這個時候last_complete與last_update之間的就會加到missing列表,而且peering完成後會持久化last_complete和last_update
5)當有新的寫入時,仍然是由Primary負責處理,會更新last_update,副本上會同時更新last_complete,與此同時,Primary會進行恢復,就是從其餘副本上拉取對象數據到本身這裏進行恢復,每當恢復完一個時,就會更新本身的last_complete(會持久化的),當全部對象都恢復完成後,last_complete就會追上last_update了。
6)當恢復過程當中,Primary又掛了再起來恢復時,先讀出本地pglog時就會根據本身的last_complete和last_update構建出missing列表,而在peering的時候對比權威日誌和本地的pglog發現權威與本身的last_update都同樣,peering的過程當中就沒有新的對象加到missing列表裏,總的來講,missing列表就是由兩個地方進行構建的:一個是osd啓動的時候read_log裏構建的,另外一個是peering的時候對比權威日誌構建的;spa

2.2Replica故障恢復

recover-replica
與Primary的恢復相似,peering都是由Primary發起的,Replica起來後也會根據pglog的last_complete和last_update構建出replica本身的missing,而後Primary進行peering的時候對比權威日誌(即自身)與故障replica的日誌,結合replica的missing,構建出peer_missing,而後就遍歷peer_missing來恢復對象。而後新的寫入時會在各個副本上更新last_complete和last_update,其中故障replica上只更新last_update,恢復過程當中,每恢復完一個對象,故障replica會更新last_complete,這樣全部對象都恢復完成後,replica的last_complete就會追上last_update。
若是恢復過程當中,故障replica又掛掉,而後重啓後進行恢復的時候,也是先讀出本地log,對比last_complete與last_update之間的pglog記錄裏的對象版本與本地讀出來的該對象版本,若是本地不是最新的,就會加到missing列表裏,而後Primary發起peering的時候發現replica的last_update是最新的,peering過程就沒有新的對象加到peer_missing列表裏,peer_missing裏就是replica本身的missing裏的對象。線程

相關文章
相關標籤/搜索