ceph基于pglog的一致性協議
來自: http://www.bitstech.net/2016/02/19/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,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來處理,這里就不多說了。
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故障恢復
簡單起見,圖中的數字就表示pglog里不同對象的版本。
1)正常情況下,都是由Primary處理client端的I/O請求,這時,Primary和Replicas上的last_update和last_complete都會指向pglog最新記錄;
2)當Primary掛掉后,會選出一個Replica作為“臨時主”,這個“臨時主”負責處理新的讀寫請求,并且這個時候“臨時主”和剩下的Replicas上的last_complete和last_update都更新到該副本上的[pglog的最新記錄;
2.2Replica故障恢復
與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里的對象。