淺談分布式存儲系統中的數據一致性要求
前言
分布式存儲是近幾年的熱門話題之一,它和傳統SAN/NAS存儲的區別是,分布式存儲使用標準硬件(比如x86服務器和10GbE網絡),而傳統SAN/NAS存儲使用的是專有硬件。使用標準硬件的好處是通用,不會受限于產商,而且成本上也更便宜,還可以做到按需擴容。
存儲系統有一大鐵則,即非不可抗力情況下不能發生數據丟失,亦即要求數據可靠一致——這往往也被稱之為存儲系統的生命線或底線。分布式存儲因物理結構跟傳統存儲的不同,會有不一樣的實現數據高可靠的方法。本文就這個話題做一些初步探討。
在進一步之前,我們先來明確一下概念,本文中所說的“分布式存儲”,是指采用標準x86服務器和網絡互聯,并在其上運行相關存儲軟件的系統。近幾年云計算是熱門之一,“云存儲”也是大家常見的名詞。在國內云計算平臺常見的就是OpenStack,而云存儲往往是指結合OpenStack使用的分布式存儲,如Ceph、GlusterFS或Sheepdog等。
除了“云計算”和“云存儲”之外,ServerSAN也是近幾年熱點之一,它和“云存儲”概念上比較接近,也是通過一組x86服務器互聯,并對外提供傳統SAN-like的存儲接口。國內幾個涉足ServerSAN的廠商,也是通過包裝Ceph等分布式存儲系統對外提供產品或服務的。
緣起
為了提供數據可靠性,分布式存儲系統一般通過數據存儲多個副本(一般是3個副本)或者EC(本質上也是副本)來實現。比如用戶存儲一個txt文檔,在底層分布式存儲系統中,這份文檔會被存儲3個副本,并放置在不同故障域的不同硬盤上。這樣即使損壞一塊硬盤,數據不會丟失。即使同時損壞不同故障域的兩塊硬盤,數據仍然不會丟失。不過在硬盤損壞后,存儲系統一般會及時感知并補全丟失的副本。
多副本帶來了數據的可靠性,同時也帶來了一致性方面的問題。比如給定數據A的三個副本A1、A2和A3,如何能保證它們的內容是一致的。如果內容不一致,往往代表出現了問題。比如:
- A1內容:hello world
- A2內容:hello wor
- A3內容:hello w
假設A1是用戶真正寫入的數據,如果A1突然損毀,此時即使有A2、A3兩個副本在,數據仍然發生了丟失。
“承諾”和“遵守”
那么如何保證副本間的數據一致性?
首先,先確定合法的數據。這里的關鍵詞是“承諾”。比如用戶要寫入“hello world”,數據通過網絡發給存儲系統,在存儲系統沒有反饋之前,用戶不能確定寫入是否成功,更不能假設寫入已經成功。只有當存儲系統反饋給用戶“你的hello world寫入成功”之后 ,數據才算寫入成功 —— 這里稱之為“承諾”,即底層分布式存儲系統承諾數據已經寫入,且由多副本機制保護,不會出現錯亂或丟失,用戶能夠讀到自己之前寫入的數據。
其次,分布式存儲系統要遵守“承諾”。在反饋寫入成功之后,即使發生部分副本硬件損壞,也不能發生數據丟失。如果出現上述例子中A1損壞,則就是數據丟失,因為余下A2、A3的數據都是跟“承諾”不一樣的。
如何做到“遵守”,是分布式存儲系統的核心之一。我們舉例說明這個問題,以Ceph為例,數據寫入請求最終是要發送給三個副本,不過Ceph為這三個副本建立了一主兩從的主從關系,數據會單獨發送給主副本,再由主副本轉達給另外兩個從副本。另外數據在三個副本節點上寫入的時候,都會先以直寫方式寫入本地Journal,然后再以非直寫的方式寫入數據盤。
這里有兩個疑問:
1. 為什么采用“一主兩從”這樣的主從模式?
2. 為什么要寫Journal?
先討論***個問題“為什么采用主從模式”。首先主副本作為一個結果收集點和用戶反饋人。三個副本寫入的結果如何,需要有人統一匯總、處理并反饋給用戶。如果用戶自己來收集和處理,則相當于副本機制侵入到了用戶的業務邏輯中,明顯不合理。如果另外使用一個專門的節點Cordinator作為中心協調人,三個副本寫入的結果都先反饋給Cordinator,再由Cordinator處理和反饋給用戶,這樣缺點之一是IO路徑上多了Cordinator這一跳,則增加了每次IO的耗時。缺點之二是增加了物理資源投入。缺點之三是如果Cordinator故障后,三個副本群龍無首,且Cordinator的重新選取將面臨各種不便。
如果使用主從模式,三個副本一主兩從,首先避免了上面提到的缺點之一和缺點之二。對比上述缺點之三,主從模式下主副本故障之后,重新選主會更自然輕松。
再討論第二個問題“為什么要寫Journal”。Ceph中“臭名昭著”的double write現象,正是因為寫Journal引起的。數據副本在寫入時,要求先以直寫方式寫入Journal,然后再以非直寫方式寫入文件。如此成本高昂,卻仍不得不為之,正體現了Ceph存儲系統對數據可靠這一生命線的尊重。因為Ceph Journal的主要作用類似于數據庫中的WAL(Write Ahead Log),提供數據寫入的原子性,避免故障時造成無法回溯的中間數據。
不過,進一步思考,會產生新的疑問。Journal能避免故障時產生中間數據,即使用Journal之后,數據寫入要么完全成功,要么完全失敗,不會部分成功。但我們仍然不能判定故障恢復后,副本數據分別是處于“完全成功”還是“完全失敗”。Ceph是通過pglog來決定的,pglog由主副本生成并在副本間同步的,它包含了本次數據寫入的版本號,并也會被持久化到Journal中。因此故障后副本會比較數據中的版本信息和Journal中的版本信息,由此判定是“完全成功”還是“完全失敗”,為集群級別的故障恢復流程提供明確的輸入。
綜上所述,“遵守”“承諾”并非易事,從其中可見Journal至關重要,沒有Journal的分布式存儲系統,其數據一致性都將存疑。
結語
本文嘗試從日常思維的角度,簡述多副本機制的必要性和帶來的數據一致性挑戰,并舉例Ceph如何應對這個挑戰。不過說來容易做來難,分布式存儲系統中,編程實踐也尤為重要。