問題的提出
在電腦科學領域,分散式一致性問題是一個相當重要,且被廣泛探索與論證的問題,通常存在於諸如分散式檔案系統、快取系統和資料庫等大型分散式儲存系統中。
什麼是分散式一致性?分散式一致性分為哪些類型?分散式系統達到一致性後將是一個什麼樣的狀態?如果失去了一致性約束,分散式系統是否還可以依賴?如果一味地追求一致性,對系統的整體架構和效能又有多大影響?這一系列的問題,似乎都沒有一個嚴格意義上準確的定義和答案。
✤ 終端使用者
IT 技術的發展,讓我們受益無窮,從日常生活的超市結帳到火箭發射,現代社會中幾乎所有企業,都離不開電腦技術的支援。
儘管電腦工程師們創造出了很多高科技的電腦產品來解決我們日常碰到的問題,但使用者只會偏好選擇一些好用的產品,那些難以使用的電腦產品最後都會被淘汰--這種好用性,其實就是使用者體驗的一部分。
電腦產品的使用者體驗,可以分為便捷性、安全性和穩定性等方面。在本書中,我們主要討論的是使用者在使用電腦產品過程中遇到的那些和一致性有關的問題。在此之前,我們首先來看一下電腦產品的終端使用者是誰,他們的需求又是什麼。
✤ 火車站售票
假如說我們的終端使用者是一位經常坐火車的旅行家,通常他是去車站的售票處購買車票,然後拿著車票去剪票口,再坐上火車,開始一段美好的旅行--一切似乎都是那麼和諧。想像一下,如果他選擇的目的地是台南,而某一趟開往台南的火車只剩下最後一張車票了,可能在同一時刻,不同售票視窗的另一位乘客也購買了同一張車票。假如說售票系統沒有進行一致性,兩人都購票成功了。而在剪票口剪票的時候,其中一位乘客會被告知他的車票無效--當然的台灣的鐵路售票系統已經很少出現這樣的問題了,但在這個實例中,我們可以看出,終端使用者對於我們的系統的需求非常簡單:
“請售票給我,如果沒有票了,請在售票的時候就告訴我沒有票。"
這就對購票系統提出了嚴格的一致性要求--系統的資料(在本例中指的就是那趟開往台南的火車的剩票數),無論在哪個售票視窗,每時每刻都必須是準確無誤的!
✤ 網上購物
假如說我們的終端使用者是一名網上購物狂,當他看到一件庫存量為5 的心儀商品,會迅速地確認購買,寫下收貨地址,然後下單。然而,在下單的那個瞬間,系統可能會告知該使用者:“庫存量不足!”此時,絕大部分的消費者常常都會抱怨自己動作太慢,使得心愛的商品被其他人搶走了!
但其實有過網購系統開發經驗的工程師一定明白,在商品詳情頁面上顯示的那個庫存量,通常不是該商品的真實庫存量,只有在真正下單購買的時候,系統才會檢查該商品的真實庫存量。但是,誰在意呢?
在上面三個實例中,相信讀者一定已經看出來了,我們的終端使用者在使用不同的電腦產品時對於資料一致性的需求是不一的:
有些系統,既要快速地回應使用者,同時還要保障系統的資料對於任意用戶端都是真實可靠的,就像火車站的售票系統。
還有些系統,需要為使用者保障絕對可靠的資料安全,雖然在資料一致性上存在延遲時間,但最後務必保障嚴格的一致,就像銀行的轉帳系統。
另外的一些系統,雖然向使用者展示了一些可以說是「錯誤」的資料,但是在整個系統使用過程中,一定會在某一個流程上對系統資料進行準確無誤的檢查,進一步避免使用者發生不必要的損失,就像網購系統。
✤ 更新的平行處理性
在電腦發展的早期階段,受到底層硬體技術的限制,同時也是由於人們對於電腦系統的實際使用需求比較簡單,因此很多上層的應用程式架構都是單執行緒模型的。以C 語言為例,其誕生於上世紀70 年代,當時幾乎所有使用C 語言開發的應用程式都是單執行緒的。從現在來看,單執行緒應用程式雖然在執行效率上無法和後來的多執行緒應用程式相比,但是在程式設計模型上相對簡單,因此能夠避免多執行緒程式中出現的不少平行處理問題。
隨著電腦底層硬體技術和現代作業系統的不斷發展,多執行緒技術開始被越來越多地引用到電腦程式設計模型之中,並對現代電腦應用程式的整體架構有著非常重要的作用。
多執行緒的引用,為應用程式在效能上帶來卓越提升的同時,也帶來一個
最大的副作用--平行處理。《Computer Systems: A Programmer’s
Perspective》一書對平行處理定義如下:如果邏輯控制流在時間上重疊,那麼它們就是平行處理的。這裡提到的邏輯控制流,通俗地講,就是一次程式操作,例如讀取或更新記憶體中變數的值。
在本書後面的討論中,我們提到的“平行處理”都特指更新操作的平行處理,即有多個執行緒同時更新記憶體中變數的值--我們將這一現象稱為更新的平行處理性。
✤ 分散式一致性問題
在分散式系統中另一個需要解決的重要問題就是資料的複製。在我們日常的開發經驗中,相信很多開發人員都碰到過這樣的問題:假設用戶端C1 將系統中的值K 由V1 更新為V2,但用戶端C2 無法立即讀取到K 的最新值,需要在一段時間之後才能讀取到。讀者可能也已經猜到了,上面這個實例就是常見的資料庫之間複製的延遲時間問題。
分散式系統對於資料的複製需求一般都來自以下兩個原因:
■為了加強系統的可用性,以防止單點故障引起的系統不可用。
■提升系統的整體效能,透過負載平衡技術,能夠讓分佈在不同地方的資料備份都能夠提供給使用者服務。
資料複製在可用性和效能方面給分散式系統帶來的極大好處是不言而喻的,然而資料複製所帶來的一致性挑戰,也是每一個系統研發人員不得不面對的。
所謂的分散式一致性問題,是指在分散式環境中引用資料複製機制後,不同資料節點間可能出現的,並無法依靠電腦應用程式本身解決的資料不一致情況。簡單地講,資料一致性就是指在對一個備份資料進行更新的同時,必須確保也能更新其他的備份,否則不同備份之間的資料將不再一致。
那怎麼來解決這個問題呢?順著上面提到的複製延遲時間問題,很快就有人想到了一種解決辦法,那就是:
“既然是由於延遲時間引起的問題,那我可以將寫入的動作阻塞,直到資料複製完成後,才完成寫入動作。"
沒錯,這似乎能解決問題,而且有一些系統的架構也確實直接使用了這個想法。但這個想法在解決一致性問題的同時,又帶來了新的問題:寫入的效能。如果你的應用場景有非常多的寫入請求,那麼使用這個想法之後,後續的寫入請求都將阻塞在前一個請求的寫入操作上,導致系統整理效能急劇下降。
整體來講,我們無法找到一種能夠滿足分散式系統所有系統屬性的一致性解決方案。因此,如何既保障資料的一致性,同時又不影響系統執行的效能,是每一個分散式系統都需要重點考慮和權衡的。於是,就出現了以下一致性等級:
■ 強一致性
這種一致性等級是最符合使用者直覺的,它要求系統寫入什麼,讀出來的也會是什麼,使用者體驗好,但實現起來常常對系統的效能影響比較大。
■ 弱一致性
這種一致性等級約束了系統在寫入成功後,不承諾立即可以讀到寫入的值,也不實際承諾多久之後資料能夠達到一致,但會盡可能地保障到某個時間等級(例如秒等級)後,資料能夠達到一致狀態。弱一致性還可以再進行細分:
階段一致性:該一致性等級只保障對於寫入的值,在同一個用戶端階段中可以立即讀到一致的值,但其他的階段不能保障。
使用者一致性:該一致性等級只保障對於寫入的值,在同一個使用者中可以立即讀到一致的值,但其他使用者不能保障。
■ 最後一致性
最後一致性是弱一致性的特例,系統會保障在一定時間內,能夠達到一個資料一致的狀態。這裡之所以將最後一致性單獨提出來,是因為它是弱一致性中非常重要的一種一致性模型,也是業界在大型分散式系統的資料一致性上比較推崇的模型。
本書將從分散式一致性的理論出發,向讀者說明幾種典型的分散式一致性協定是如何解決一致性問題的。之後,則會深入介紹分散式一致性問題的工業解決方案--ZooKeeper,並注重介紹這一分散式協調架構的使用方法、內部實現以及運行維護技巧。