常見三副本kv存儲系統架構介紹
目前很多常見的分布式存儲系統都是基於三副本機制的,如ceph、小米的飛馬系統等
一般由三個部分組成,實現kv存儲層(如ceph的osd)、client端(發起io操作)以及mds(metadata server)
- kv存儲層實現持久存儲和io復制
- client實現io分發
- mds管理包括kv存儲層進程狀態、數據分布
數據分布算法
hash算法根據數據的key將數據映射到物理存儲節點上,這種作為在存儲節點擴減容時,會導致大量數據遷移。
一致性hash是將物理節點組成一個環,計算出hash值后,將數據路由到hash值后面最近的一個節點上,這樣當一個節點失效時,數據只用遷移到下一個節點上。
這種物理節點直接參與映射的方式,可能導致數據傾斜,所以引入虛擬節點(ceph中的pg),虛擬節點數目為物理節點的數倍。引入虛擬機節點有兩個好處:
- 數據是存放在固定虛擬節點上的
- 我們管理虛擬節點到物理節點的映射時,可以根據物理節點是否在同一個機架等來影響數據的分布,減小故障域。
- 每個虛擬節點組成一個復制組,各虛擬節點之間數據沒有關聯,這樣可以增加並發。
一次寫io過程
- mds節點管理着虛擬節點到數據副本的映射關系,包括虛擬節點對應的各個數據副本所在存儲節點,各副本的狀態信息。
- client節點緩存着虛擬節點到主副本信息,kv存儲層緩存着虛擬節點到主副本信息和虛擬節點的三副本信息。
這些信息都是從mds查詢的
- client節點根據數據的key計算出虛擬節點,在根據虛擬節點找到主副本物理節點,然后將io發送給主副本節點。
- 主副本節點收到請求后,將數據寫入本地,同時復制給其他副本,等到副本都寫成功后,響應給client。
常見三副本存儲系統如何保證數據一致性呢?
有如下三個要素:
- 基於日志復制的方式,保證日志在各副本順序一致,那么根據日志回放出來的數據,最終狀態也將趨於一致。
- 主副本會將更新操作發送到各個副本,只有各個副本都回復寫成功后,才會響應client端。
如果一切正常,上面兩點足夠保證數據一致了,因為從client的視角看,各副本是強一致的,發生主副本切換,也不影響數據一致性。
- 主副本故障時,mds選新主副本時,必須選狀態正常的副本。(領導選舉)
這里說的狀態正常即新的主與原來的主數據一致。
上面保證了數據在副本之間的一致性,另外還要關注出現網絡分區副本之間出現腦裂時,如果保證線性一致性,不會發生stale read
一條條解釋
請求ID
主副本對每個更新操作維護一個ReqID,收到更新操作ReqID++
任期
每次主副本切換,看成是一個新的任期,TermID++,ReqID置0
備副本切換,需要更新任期嗎??? --- 不需要,TermID只關心主副本
<TermID, ReqID>可以唯一定位一個更新操作
日志順序一致
日志:記錄了更新操作(寫、刪)的位置、長度、操作類型以及<TermID, ReqID>。可以看到日志中沒有記錄數據,所以,日志可以增加一個狀態,表示數據部分是否以及落盤。注意日志本身也是持久化存儲的,可以放在高速ssd或者nvram中。
- 主副本收到更新請求,會檢查client端發送來的請求TermID是否與自己的相同,相同時,會自增ReqID(也會持久化),並本地記錄一條日志(日志為未提交狀態);然后觸發數據本地落盤和發送給備同時進行
TermID不同時,會觸發TermID較小的一方反查映射信息
本地持久化成功后,回調中將日志提交
- 備副本收到請求時,首先檢查TermID與主副本的是否一致,如果自己的老就反查視圖,否則拒絕請求。
如果TermID一致,會檢查自己的ReqID的連續性,如果比收到的主副本的僅僅小1,剛剛好,如果比主副本小很多,說明丟了消息,拒絕;如果不小於收到的,說明是重入消息接受。
等於1的時候 是否要接受??? -- 接受,但不更新本地持久化的reqid
備副本異常
各個副本節點都和mds保持心跳,每秒一次,5次mds將這個副本設置為異常。
- 主副本將數據復制到備副本時,有超時重試機制,等待備回復ok
- 如果備副本異常了,mds會通知主副本,備已經異常,無需等待這個副本回復。
這里非常重要,是mds通知主副本備異常了,所以如果這個場景,發生切主時,mds一定不會選擇這個異常的備
備副本異常恢復
備副本從故障中恢復時,由於有部分io自己沒有,這種狀態的備會被mds標記為catchup狀態,它也沒有升主資格。
當這個備catchup完成時,會將自己catchup完成上報給mds,mds將起狀態標記為正常,恢復了升主資格。
catchup過程:
- 備先根據自己本地log中最新的<TermID, ReqID>去主副本上確認自己缺少哪些數據,然后從主上將這些數據讀回來,寫到本地
- 在第一步的過程中,備副本收到的新io會記錄到另一片區域(避免與catchup io發生寫覆蓋),等第一步完成后,再將這部分數據寫到真正的位置上。
- 在第一二步都完成后,向mds上報自己catchup完成。
映射關系變更
主副本異常后,會從正常的備中選一個新主;無論是主、備,只有在異常達到一定時間后才會被隔離出集群。
主副本異常與mds心跳斷開幾秒后,mds會從正常的備中選一個當作新主
從以上介紹中,可以看出mds在備副本是否具有升主資格是絕對可靠的,所以在切主時,一定不會將主切到一個數據與老主不一致的副本上。
一個異常場景
主副本日志狀態未提交,備副本數據落盤成功並且日志提交了
- 此時全局掉電會發生什么
全局掉電時,各副本所在服務器都掛了,各副本起來之后不會發生切主操作 ? 關注一下這種場景數據怎么恢復的
主還是老主,備進行catch up時,主會將未提交的日志復制給備,備把本地已提交的日志改為未提交 ???
- 此時主副本網絡閃斷會發生什么
切主,之前的寫請求,備上已提交,老主恢復后,向新主拿數據,本地未提交的日志會被改為提交??