當前位置︰首頁 > 新聞資訊 > 正文

淺談“完美無瑕”的分布式系統2019-08-28 09:44:00 | 編輯︰hely | 查看︰ | 評論︰0

實際上,分布式系統並不一定是並行的。Lena Hall 給我們詳細闡述了“完美無瑕”的分布式系統應該是什麼樣的,以及我們應該如何朝著這一目標做哪些工作。

本文最初發布于 Lena Hall 個人博客,譯者︰劉志勇 來源︰InfoQ

導讀︰ 本文基于 Lena Hall 的 O’Reilly Velocity 2019 主題演講,進行了解讀。分布式系統是建立在網絡之上的軟件系統。正是因為軟件的特性,所以分布式系統具有高度的內聚性和透明性。因此,網絡和分布式系統之間的區別更多的在于高層軟件(特別是操作系統),而不是硬件。內聚性是指每一個數據庫分布節點高度自治,有本地的數據庫管理系統。透明性是指每一個數據庫分布節點對用戶的應用來說都是透明的,看不出是本地還是遠程。在分布式數據庫系統中,用戶感覺不到數據是分布的,即用戶不須知道關系是否分割、有無副本、數據存于哪個站點以及事務在哪個站點上執行等。說了這麼多,但實際上,分布式系統一點都不復雜,簡單的說就是︰將整個個軟件視為一個系統(不管它有多復雜),然後將整個系統分割為一系列的進程,每個進程完成一定的功能。再將這些進程分散到不同的機器上,分散後,選擇若干種通信協議將它們連接起來。這就完成了分布式系統的構建。人們常常把分布式系統自然而然的和並行計算聯系起來。然而這並不正確。實際上,分布式系統並不一定是並行的。Lena Hall 給我們詳細闡述了“完美無瑕”的分布式系統應該是什麼樣的,以及我們應該如何朝著這一目標做哪些工作。

 

 

本文闡述了我們所做工作帶來的影響,我們所面臨的復雜性和障礙,以及對于構建更好的分布式系統的重要因素,尤其是當其他關鍵領域依賴並建立我們所創造的基礎之上時。

目前可用的系統已經提供了許多解決方案,封裝了許多分布式算法,實現了自動化並抽象出一些復雜性。使用這些系統的工程師並不一定非要擁有開發它們所需的大量知識不可。盡管對于工程師新手來說,了解這些系統所依賴的基礎知識越來越沒有什麼必要了,但在某些情況下,了解幕後工作對于做出正確的決策並解決出現的困難問題還是必不可少的。

基礎知識仍然很重要嗎?

為什麼說重視基礎知識很重要?今天,我們正處于這樣的一個階段,分布式系統在醫療系統、自主設備、傳輸自動化和其他生命攸關系統中的應用越來越廣泛,在這些場景中,錯誤的代價越來越高,而正確性變得日益重要起來。

錯誤的代價並不是指你的系統今天不可用的時間長短。它是關于你的系統發生宕機或故障時,給用戶或他們的用戶帶來的代價。我們應該負起責任,並永遠記住我們為什麼這樣做,我們真正要解決的問題是什麼。

 

 

每個行業都希望通過結合他們和我們的研究和解決方案來取得更大的進步。而我們的工作能夠幫助其他領域更好地實現他們的目標。對我們來說,了解如何放寬某些限制或者微調某些權衡也是非常有用的。

了解核心的內容,是一個強大的工具,可以讓我們從容駕馭不斷變化的選項和工具的復雜性,並幫助我們構建正確的解決方案,以改進我們現有的選項。

但事實證明,要做到這一點並不是那麼容易。在我們前進的道路上,荊棘載途,艱難險阻。

理論和實踐存在巨大的差距

理解“正確”對我們的系統意味著什麼,對我們來說,不啻為一個挑戰。大多數理論材料都很不“接地氣”,眾所周知,它們很難被理解並掌握。不止如此,它往往還不包括那些將這一理論付諸實踐所需的信息。要想應用到生產系統中,就必須修改理論算法,並對它們進行調整以適應實際環境。其中許多並沒有透露對實際解決方案很重要的具體細節。甚至對協議細節稍有誤解,也會破壞協議的正確性。因此,我們需要做更多的額外工作,以保證實踐時,協議仍然是正確的。

 

 

正確性難以驗證和維持

在實際環境中驗證並維持分布式系統的正確性,是一項具有挑戰性的工作。它可能听上去很完美,但在實踐中,卻可能是低效的、或者難以實現的。這在理論上听起來不切實際,但在實踐中卻是完全可以接受的。須知在算法邏輯及其實現中,很多地方都有可能出錯,而且還難以檢測。

正確性並不總是優先考慮的事

另一方面,正確性並不總是優先考慮的事。還有截止期限、競爭和客戶都需要更快的解決方案。終端系統還可能會出現倉促而不能正確修復的情況。這意味著團隊可能沒有足夠的時間來做這件事︰正確地討論並系統地解決罕見的“間歇性”錯誤背後的真正原因,這些錯誤在未來還會再次發生,導致出現更多的錯誤。

我們該如何改進?

我們可以從多個方面改進。其中之一就是︰強調並集中精力提高正確性,以確保我們能夠構建並維護系統,使其始終按照我們所希望的那樣運行。

另一個就是,提高對它們如何工作的理解,因為這有助于我們減少復雜性和可能出現的錯誤,並使我們更有準備迎接即將到來的挑戰。

當我們不直接實現分布式算法和概念時,我們肯定會依賴基于那些構建的系統。在我們構建的內容與其他領域相互交叉時,理解基本概念和權衡就變得極為重要了。

如果向你承諾了一些性能和一致性,那麼你該如何確保這些承諾就是在你需要的確切水平上提供的呢?

簡單問題復雜化

當幾台計算機相互通信時,瑣碎的問題就變成了棘手的問題,而且它們還會累積起來。分布式系統難以理解,也難以實現,而且在實踐中很難保持它們的正確性。

我認為有很多方法可以說明其原因。最近,我有機會為生物信息學領域的人解釋,他們想知道為什麼需要在分布式環境中的重要屬性之間進行權衡。

排序

我想到的第一件事就是對事件進行排序。在一台機器上進行排序很容易,但當消息通過網絡發送時,就變得很難了!我們不能依賴物理時間戳,因為不同機器上的物理時鐘往往有所偏移。對于分布式系統中的排序,我們經常應用邏輯時鐘,或者簡單地說,在節點之間傳遞的計數器。

由于分布式系統的異步特性,我們並不能很容易地為所有事件建立競爭順序,因為其中一些事件是並發的!我們所能做的就是找出哪些事件是並發的,哪些事件在彼此之前發生。即使是這樣一個簡單的任務,我們也需要做出一些決定。

例如,如果系統告訴我們事件是有序的,但實際上它們是並發的,那麼,這種情況我們是否可以接受呢?

 

 

或者,我們是否需要確切地知道,當我們可以對事件進行排序時,事件真的不是同時發生的嗎?

 

 

協議

我們不能簡單地對並發事件進行排序,有時候,我們仍然需要決定操作的順序、值、值序列或其他任何東西。

事實證明,讓幾台機器選擇相同的東西是另一種情況。在這種情況下,我們必須要問自己的問題,並決定什麼對我們是合適的。

 

 

例如,二階段提交(Two Phase Commit)的是一種解決方案,在這種解決方案中,我們的節點可以就某些方面達成一致。

譯注︰在計算機網絡以及數據庫領域內,二階段提交(Two-phase Commit)是指,為了使基于分布式系統架構下的所有節點在進行事務提交時保持一致性而設計的一種算法。通常,二階段提交也被稱為是一種協議。在分布式系統中,每個節點雖然可以知曉自己的操作時成功或者失敗,卻無法知道其他節點的操作的成功或失敗。當一個事務跨越多個節點時,為了保持事務的 ACID 特性,需要引入一個作為協調者的組件來統一掌控所有節點(稱作參與者) 的操作結果並最終指示這些節點是否要把操作結果進行真正的提交(比如將更新後的數據寫入磁盤等等)。因此,二階段提交的算法思路可以概括為︰ 參與者將操作成敗通知協調者,再由協調者根據所有參與者的反饋情報決定各參與者是否要提交操作還是中止操作。

 

 

只要我們的節點不出故障,它就可以進行工作。

如果一些節點發生崩潰,為了防止出現數據不一致的可能性,系統就會阻塞,直到崩潰的節點返回,這種情況也有可能永遠不會發生;或者需要非常、非常長的時間。

所以,這個算法是安全的,但它不是實時的。

如果這還不是我們所能接受的,那麼我們是不是真的需要一個系統來回應呢?

 

 

在這種情況下,我們可能有另一種潛在的解決方案︰三階段提交(Three Phase Commit),當節點出現故障時,它就不會發生阻塞。

譯注︰ 三階段提交(Three-phase commit),是在計算機網絡及數據庫的範疇下,使得一個分布式系統內的所有節點能夠執行事務的提交的一種分布式算法。三階段提交是為解決兩階段提交協議的缺點而設計的。與二階段提交不同的是,三階段提交是 “非阻塞” 協議。三階段提交在二階段提交的第一階段與第二階段之間插入了一個準備階段,使得原先在二階段提交中,參與者在投票之後,由于協調者發生崩潰或錯誤,而導致參與者處于無法知曉是否提交或者中止的 “不確定狀態” 所產生的可能相當長的延時的問題得以解決。

 

 

但是,當有網絡分區時,情況又會怎麼樣呢?

 

 

網絡的兩個相互隔離的分區在超時後可能會做出兩個不同的決定,系統最終將會處于不一致的狀態。

所以,這時候我們得到的是相反的結果︰系統是實時響應的,但並不安全,因為不同的節點可以決定不同的值。

 

 

如果我們對這兩個選項中的任何一個都能接受,那就太好了!

如果我們希望數據始終保持一致性,並且系統還要能夠響應,那麼我們應該做什麼呢?

不可能結果

不可能結果 證明,實際上,在完全異步的環境中,總是終止並最終作出決定的確定性算法是不存在的,甚至可能出現一次故障。

譯注︰ 不可能結果(Impossibility result)是分布式領域的專業術語,在一個完全異步的消息傳遞分布式系統中,如果一個進程有故障,那麼一致性問題是無法得到解決的。簡單來說,就是在一定的限制條件下問題能否被解決,那麼任務的不可能結果就只有兩種情況︰能和不能。

 

 

從這個結果中,我們可以學到的主要內容是︰如果我們想在實踐中解決協議,我們就必須重新考慮我們的假設,以反映更為現實的期望!例如,我們可以設定最大消息延遲的上限,並確定系統可接受的失敗次數。

如果我們改變假設,那麼我們就可以用多種方式來解決分布式協議!

 

 

Paxos 算法

最著名的解決方案是 Paxos 算法,眾所周知,這個算法晦澀難懂,而且難以正確實施。

它實際上是可行的,但只有在大多數節點都必須啟動,且最大消息延遲是有限的情況下才可行。

在 Paxos 中,任何節點都可以提出一個值,在經過“準備”和“建議”階段後,所有節點都應該就相同的值達成一致。

大多數節點都需要啟動,因為如果在每個階段中 Quorums 相交,總有有至少一個節點記得最近的建議是什麼,這就阻止了對舊值達成一致。

 

 

為了提高算法的效率,在實際應用中,對出事算法進行了許多優化。基于它們所選擇的權衡,共識算法也有許多可能的變體。

例如,Leader 完成了多少工作。擁有一個強大的 Leader 可能是好事,但也有可能是壞事。這要取決于失敗的頻率和重新選舉的難度。另一種權衡是,我們可以容忍多少節點出故障,以及 Quorum 數量應該有多少。

有些被低估的標準是算法在實踐中的可理解性和可實施性。Raft 之所以廣受歡迎,是因為它更容易理解,現在已經應用于許多廣泛使用的項目中。

譯注︰ Paxos 算法是 Leslie Lamport(就是 LaTeX 中的 “La”,此人現在在微軟研究院)于 1990 年提出的一種基于消息傳遞的一致性算法。這個算法被認為是類似算法中最有效的。Paxos 算法解決的問題是一個分布式系統如何就某個值(決議)達成一致。一個典型的場景是,在一個分布式數據庫系統中,如果各節點的初始狀態一致,每個節點執行相同的操作序列,那麼他們最後能得到一個一致的狀態。為保證每個節點執行相同的命令序列,需要在每一條指令上執行一個 “一致性算法” 以保證每個節點看到的指令一致。

新的權衡仍在不斷發現

但更有趣的是,盡管共識和協議的話題並不新鮮,但我們仍然發現了許多新的優化和權衡。

在經典的 Paxos 算法中,大多數節點都需要啟動以確保所有的 Quorum 相交。但事實證明,我們可以重新考慮並簡化大多數 Quorum 要求。事實證明,只有準備階段和建議階段的 Quorum 相交就足矣,這為我們提供了更多的靈活性來對每個階段的 Quorum 大小和性能進行實驗!

 

 

重點是,揭示迄今為止的新性能和可用性權衡,有助于我們在實踐中擴大選擇範圍。共識只是一個構件塊,但它可以用來解決許多常見問題,如原子廣播(atomic broadcast)、分布式鎖(distributed locks)、強一致性復制等等。

請查看 Heidi Howard 博士的論文 “Distributed Consensus Revised”,該篇論文是這方面最好的論文之一。

一致性復制?

復制(Replication)是當今任何分布式系統的重要組成部分。

我們實際上可以用共識來實現強一致性復制,但它的缺點之一就是性能。另一方面當然是一致性復制,這種復制速度非常快,但在客戶端卻可以看到不一致的數據。在實踐中,我們通常會希望獲得更好的性能,同時還要保持更強的一致性,這可能有些棘手。

因此,在某些情況下,我們可以提出比共識更快的解決方案,而且比最終的一致性更加一致。

其中一個有趣的例子是 Aurora,它避免了對 I/O 和其他一些操作達成共識。它們使用 Quorum 進行寫入,但不用它們來閱讀。實際上,副本可能會存儲不同的值,但當客戶端執行讀取操作時,由于數據庫維護一致性點,因此它可以直接查看已知數據一致的節點,並將正確的數據返回給客戶端。

 

 

無沖突可復制數據類型

另一個有趣的例子是無沖突可復制數據類型(Conflict Free Replicated Data Type,CRDT)。它們可以提供強大的最終一致性︰快速讀寫操作,甚至在網絡分區期間仍然保持可用,而無需使用共識或同步。然而,只有在我們能夠有解決任何並發沖突的規則時才有可能。

換句話說,如果可以使用某些函數合並並發更新,這些可以按任意順序應用它們,並且還可以按照我們想要的任意次數應用它們,而不會破壞結果,那麼我們就只能使用這種技術。

這是一個完全可以接受的示例,其中所有更新都是附加的,因此它們完全滿足這一要求。

 

 

另一方面,這一點並不明顯,因為我們沒有明確的規則來解決這種同時更新的沖突。

 

 

Azure Cosmos DB 使用 CRDT 在並發多主多區域寫入的後台來解決沖突。Redis 和 Riak 也使用 CRDT。

 

 

故障檢測

如果我們在分布式系統中進入另一個主題,我們總會發現需要進行更多的權衡。

故障檢測器 是分布式系統中發現節點崩潰的關鍵技術之一。它們可以應用于協議問題、Leader 選舉、組員協議和其他領域。

我們可以通過故障檢測器的“完備性”、“正確度”來衡量它們的效率。

完備性顯示了系統中的一些或所有節點是否發現所有故障。正確度度量故障檢測器在懷疑另一個節點故障可能出現的錯誤級別。

事實證明,即使是不可靠的故障檢測器在實際系統中也是非常有用的,因為我們可以通過添加一種故障知識傳播到所有節點的“八卦”機制,來提高它們的完備性。

為什麼所有這一切都很重要?

權衡可能是多種多樣的,如果我們知道如何使用它們,知道在哪里尋找它們,那麼我們就可以變得非常靈活。

 

 

許多產品都是圍繞算法和權衡而構建的。這些產品為我們做出了某些選擇,我們通過使用某些產品來做出選擇。未受教育的選擇可能會導致延遲和數據丟失。對于某些系統來說,這點可能會導致客戶流失和巨額資金。對于其他系統來說,它可能導致反應緩慢,或者操作順序錯誤,從而構成了實際的生命威脅。理解你的權衡對于做出正確的選擇、了解正確的含義以及在現實中驗證系統的正確性非常重要。

驗證與維護現實中的正確性

在我們明確了抉擇和權衡之後,我們該如何在實際系統中維護正確性呢?

系統模型檢查是驗證分布式邏輯的安全性和活性(尤其是安全性)的常用選項之一。模型檢查很有用,因為它探索了系統最終可能出現的所有狀態。現在有相當多的工具,如 TLA+ 就非常有名,除此之外還有更多的新興技術,如語義感知模型檢查。

為了驗證分布式系統的實際運行實現的正確性,僅靠模型檢查是不夠的。

很少有項目會發布關于如何維護其系統的正確性並對其進行驗證的相關信息。但其中一些項目做到了。

例如,Kafka 的各種系統測試 每天都在運行,世界上的任何人都可以檢查並查看 哪些工作符合預期,哪些工作不正常。

 

 

Cassandra 對他們的綜合測試方法寫了一篇很棒的文章。

 

 

我真的希望,會有更多的產品、項目和系統能夠對他們投入測試和正確性驗證方面的工作更加開放。

如果我們看一下準備運行生產機分布式系統需要做些什麼,就會發現有很多事情需要去做。

當然,對于小範圍場景並確保多個服務協同工作良好,單元測試和集成測試是必不可少的。但這些還不夠!我們可以使用更多的技術。模糊測試和基于屬性的測試為你的系統提供了隨機生成的輸入,以確保基本屬性基于其規範是正確的。實際上,我在 Microsoft 從事過一個關于模糊測試的項目,總體來說,這是一個非常吸引人的話題。性能測試對收集各種組件的延遲和吞吐量的數據非常有用。故障注入有助于檢查系統在故障情況下是否可用,以及預期的系統屬性是否仍然保持正確。

綜上所述,導致最嚴重錯誤的背後的大量原因,仍然是異常處理邏輯。

 

 

有些事情,我們無法完全解決。我們需要接受這樣一個事實︰在我們編寫完所有的測試和檢查之後,無論如何,錯誤都會存在。我們是人類,再加上上下文切換,因此我們不可能知曉每一件事,有太多的活動部分了。我們永遠沒有探索過新的領域,如果我們害怕離開所熟悉的領域,那麼我們就永遠不會取得進展。但是,我們絕對可以為處理意外錯誤做出更好的準備,找到模式,並試圖解決導致錯誤的原因。這就是為什麼檢測代碼很重要,可觀察性也很重要。當我們意識到這種可能性並為解決生產錯誤奠定基礎時,就不那麼可怕了。

題外話

產品變化很快,描述它們的一致性、彈性和性能的術語極其繁多。基本概念和權衡仍然存在,並不斷積累。它們在孤立的情況下並沒有什麼用處,但是了解它們,對于做出正確的選擇和在實踐中保持正確性是必不可少的。當我們的系統在特定級別的響應性和安全性有強烈要求的場景中受到信任時,正確性尤為重要。

如果你正在構建某些內容時,請捫心自問︰這會被誤解嗎?復雜性就像項目周圍的一堵防彈牆一樣,它使解釋、構建、使用和改進變得困難。試著讓你構建的系統能夠被其他人理解,因為理解有助于正確性的提高。

正確性並不容易,也不是免費的。你必須致力于此,並將其作為優先事項。不僅僅是一個願意這樣做的工程師的水平上,而是整個組織的水平。不要相信你的系統知識工作︰測試它,驗證它,並在事情失敗時做好準備,向你的用戶和客戶展示你在演示系統方面投入了哪些技術和努力。

 

 

想想那些與你工作相關但沒有得到足夠重視的重要領域吧,如果你有機會和其他領域工作的其他人聊天,那就去做,了解他們在工作中所面臨的挑戰,以及他們正在做出的權衡。虛心求教,與他們分享你的工作。這會幫助你成長為一個更優秀的工程師。

 

 

作者介紹︰

Lena Hall,Microsoft 高級軟件工程師,從事大數據和分布式系統方向。曾在 Microsoft 研究院工作。同時也是一名技術演講者、機器學習 ML4ALL 的組織者、Kafka 峰會的董事。

原文鏈接︰

上一篇︰聯通大數據 5000 台規模集群故障自愈實踐 大數據需求使用的六個Hadoop發行版下一篇︰

公眾平台

搜索"raincent"或掃描下面的二維碼

?