日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

取代ZooKeeper!高并发下的分布式一致性开源组件StateSynchronizer

發(fā)布時(shí)間:2023/12/19 编程问答 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 取代ZooKeeper!高并发下的分布式一致性开源组件StateSynchronizer 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

StateSynchronizer是開(kāi)源分布式流存儲(chǔ)平臺(tái)Pravega的核心組件。StateSynchronizer組件以stream為基礎(chǔ),對(duì)外提供一致性狀態(tài)共享服務(wù)。StateSynchronizer允許一組進(jìn)程同時(shí)讀寫(xiě)同一共享狀態(tài)而不必?fù)?dān)心一致性問(wèn)題。本文將從共享狀態(tài)和一致性的角度出發(fā),詳細(xì)描述StateSynchronizer的整體架構(gòu)、工作機(jī)制和實(shí)現(xiàn)細(xì)節(jié)。利用stream的天然特性,StateSynchronizer可以高效地確定出更新操作的全局順序,并且從邏輯上實(shí)現(xiàn)了對(duì)共享狀態(tài)的一致性更新與存儲(chǔ)。由于stream訪問(wèn)的高效與輕量,StateSynchronizer特別適用于高并發(fā)(\u0026gt;= 10000 clients) 的場(chǎng)景,并在此場(chǎng)景下可以作為替代ZooKeeper和etcd的解決方案。

StateSynchronizer設(shè)計(jì)者之一Flavio是著名開(kāi)源組件ZooKeeper的最早作者,他同時(shí)也是《ZooKeeper:分布式過(guò)程協(xié)同技術(shù)詳解》這本書(shū)的作者。

StateSynchronizer不僅是Pravega公共API的一部分,許多Pravega內(nèi)部組件也大量依賴(lài)StateSynchronizer共享狀態(tài),如ReaderGroup的元信息管理。并且我們可以基于StateSynchronizer實(shí)現(xiàn)更高級(jí)的一致性原語(yǔ),例如跨stream的事務(wù)。

開(kāi)源項(xiàng)目地址:https://github.com/pravega/pravega/tree/v0.4.0

1 背景簡(jiǎn)介

1.1 什么是StateSynchronizer(狀態(tài)同步器)

Pravega [1]既可以被想象成是一組流存儲(chǔ)相關(guān)的原語(yǔ),因?yàn)樗菍?shí)現(xiàn)數(shù)據(jù)持久化的一種方式,Pravega也可以被想象成是一個(gè)消息訂閱-發(fā)布系統(tǒng),因?yàn)橥ㄟ^(guò)使用reader,writer和ReaderGroup它可以自適應(yīng)地進(jìn)行消息傳遞。本文假設(shè)讀者已經(jīng)熟悉Pravega的有關(guān)概念,否則可以參考相應(yīng)的官方文檔 [2]和已發(fā)布的4篇專(zhuān)欄文章(見(jiàn)文末鏈接)。

Pravega實(shí)現(xiàn)了各種不同的構(gòu)建模塊用以實(shí)現(xiàn)stream相關(guān)原語(yǔ),StateSynchronizer [2]就是其中之一,目的在于協(xié)調(diào)分布式的環(huán)境中的各個(gè)進(jìn)程^2。從功能上看,StateSynchronizer為一組進(jìn)程提供可靠的共享的狀態(tài)存儲(chǔ)服務(wù):允許多個(gè)客戶(hù)端同時(shí)讀取和更新同一共享狀態(tài)并保證一致性語(yǔ)義,同時(shí)提供數(shù)據(jù)的冗余和容錯(cuò)。從實(shí)現(xiàn)上看,StateSynchronizer使用一個(gè)stream為集群中運(yùn)行的多個(gè)進(jìn)程提供了共享狀態(tài)的同步機(jī)制,這使得構(gòu)建分布式應(yīng)用變得更加簡(jiǎn)單。使用StateSynchronizer,多個(gè)進(jìn)程可以同時(shí)對(duì)同一個(gè)共享狀態(tài)進(jìn)行讀取和修改,而不必?fù)?dān)心一致性問(wèn)題 [3]。

StateSynchronizer的最大貢獻(xiàn)在于它提供了一種stream原生的一致性存儲(chǔ)方案。由于stream具有只允許追加(Append-Only)的特性,這使得大部分現(xiàn)有的存儲(chǔ)服務(wù)都無(wú)法很好地應(yīng)用于stream存儲(chǔ)的場(chǎng)景。相比于傳統(tǒng)的狀態(tài)存儲(chǔ)方案,stream原生的存儲(chǔ)使得StateSynchronizer具有以下優(yōu)點(diǎn):

  • 與常見(jiàn)的鍵值存儲(chǔ)(Key/Value Store)不同,StateSynchronizer支持任意抽象的共享狀態(tài),而不僅僅局限于維護(hù)鍵值集合。

  • 與常見(jiàn)的數(shù)據(jù)存儲(chǔ)不同,StateSynchronizer以增量的方式維護(hù)了共享狀態(tài)的整個(gè)變更歷史,而不僅僅是維護(hù)共享狀態(tài)的最新快照。這一特性不僅大大減少了網(wǎng)絡(luò)傳輸開(kāi)銷(xiāo),還使得客戶(hù)端可以隨時(shí)將共享狀態(tài)回滾到任意歷史時(shí)刻。

  • 與常見(jiàn)的狀態(tài)存儲(chǔ)不同,StateSynchronizer的服務(wù)端既不存儲(chǔ)共享狀態(tài)本身也不負(fù)責(zé)對(duì)共享狀態(tài)進(jìn)行修改,所有共享狀態(tài)的存儲(chǔ)和計(jì)算都只發(fā)生在客戶(hù)端本地。這一特性不僅節(jié)約了服務(wù)端的計(jì)算資源,還增加了狀態(tài)計(jì)算的靈活性,例如:除了基本的CAS(Compare-And-Swap)語(yǔ)義,還支持高隔離級(jí)別的復(fù)雜事務(wù)^3。

  • 與現(xiàn)有的基于樂(lè)觀并發(fā)控制(Optimistic Concurrent Control, OCC) [4] [5]的存儲(chǔ)系統(tǒng)不同,StateSynchronizer可以不依賴(lài)多版本控制機(jī)制(Multi Version Concurrent Control, MVCC) [6] [7]。這意味著即使在極端高并發(fā)的場(chǎng)景下,狀態(tài)更新的提交也永遠(yuǎn)不會(huì)因版本沖突而需要反復(fù)重試。

StateSynchronizer無(wú)意于也不可能在所有場(chǎng)景中替代傳統(tǒng)的分布式鍵值存儲(chǔ)組件,因?yàn)樗倪\(yùn)行機(jī)制大量依賴(lài)stream的特性。但是,在具有stream原生存儲(chǔ)和較強(qiáng)一致性需求的場(chǎng)景下,StateSynchronizer可能是一種比其它傳統(tǒng)鍵值存儲(chǔ)服務(wù)更為高效的選擇。

1.2 “一致性”的不同語(yǔ)義

在不同的上下文環(huán)境中,“一致性”一詞往往有著不同的語(yǔ)義 [8] [9]。在分布式存儲(chǔ)和數(shù)據(jù)高可用(High Availability)相關(guān)的語(yǔ)境下,一致性通常指數(shù)據(jù)副本(Replica)的一致性 [8]:如何保證分布在不同機(jī)器上的數(shù)據(jù)副本內(nèi)容不存在沖突,以及如何讓客戶(hù)端看起來(lái)就像在以原子的方式操作唯一的數(shù)據(jù)副本,即線性化(Linearizability) [10]。常見(jiàn)的分布式存儲(chǔ)組件往往依賴(lài)單一的Leader(主節(jié)點(diǎn))確定出特定操作的全局順序,例如:ZooKeeper [11]和etcd [12]都要求所有的寫(xiě)操作必須由Leader轉(zhuǎn)發(fā)給其它數(shù)據(jù)副本。數(shù)據(jù)副本的一致性是分布式系統(tǒng)的難點(diǎn),但卻并不是一致性問(wèn)題的全部。

脫離數(shù)據(jù)副本,在應(yīng)用層的語(yǔ)境下,一致性通常指數(shù)據(jù)滿(mǎn)足某種約束條件的不變性(Invariant)[13],即:指的是從應(yīng)用程序特定的視角出發(fā),保證多個(gè)進(jìn)程無(wú)論以怎樣的順序?qū)蚕頎顟B(tài)進(jìn)行修改,共享狀態(tài)始終處于一種“正確的狀態(tài)”,而這種正確性是由應(yīng)用程序或業(yè)務(wù)自身定義的。例如,對(duì)于一個(gè)交易系統(tǒng)而言,無(wú)論同時(shí)有多少個(gè)交易在進(jìn)行,所有賬戶(hù)的收入與支出總和始終都應(yīng)該是平衡的;又如,多進(jìn)程操作(讀/寫(xiě))一個(gè)共享的計(jì)數(shù)器時(shí),無(wú)論各進(jìn)程以怎樣的順序讀寫(xiě)計(jì)數(shù)器,計(jì)數(shù)器的終值應(yīng)該始終與所有進(jìn)程順序依次讀寫(xiě)計(jì)數(shù)器所得到的值相同。參考文獻(xiàn) [8]將這種一致性歸類(lèi)為“事務(wù)性的一致性(Transactional Consistency)”,而參考文獻(xiàn) [9]則將此類(lèi)一致性簡(jiǎn)單稱(chēng)為“涉及多對(duì)象和多操作的一致性”。應(yīng)用層的數(shù)據(jù)一致性語(yǔ)義與數(shù)據(jù)副本的一致性語(yǔ)義完全不同,即使是一個(gè)滿(mǎn)足線性化的分布式系統(tǒng),也需要考慮應(yīng)用層的數(shù)據(jù)一致性問(wèn)題^4。

1.3\tStateSynchronizer與現(xiàn)有的一致性存儲(chǔ)產(chǎn)品

目前常用的分布式鍵值存儲(chǔ)服務(wù),例如ZooKeeper和etcd,都可以看作是一種對(duì)共享狀態(tài)進(jìn)行存儲(chǔ)和維護(hù)的組件,即所有鍵值所組成的集合構(gòu)成了當(dāng)前的共享狀態(tài)。在數(shù)據(jù)副本層面,ZooKeeper和etcd都依賴(lài)共識(shí)(Consensus)算法提供一致性保證。ZooKeeper使用ZAB(ZooKeeper’s Atomic Broadcast)協(xié)議 [14]在各節(jié)點(diǎn)間對(duì)寫(xiě)操作的提交順序達(dá)成共識(shí)。在廣播階段,ZAB協(xié)議的行為非常類(lèi)似傳統(tǒng)的兩階段提交協(xié)議。etcd則使用Raft協(xié)議 [15]在所有節(jié)點(diǎn)上確定出唯一的寫(xiě)操作序列。與ZAB協(xié)議不同,Raft協(xié)議每次可以確認(rèn)出一段一致的提交序列,并且所有的提交動(dòng)作都是隱式的。在應(yīng)用層數(shù)據(jù)層面,ZooKeeper和etcd都使用基于多版本控制機(jī)制的樂(lè)觀并發(fā)控制提供最基礎(chǔ)的一致性保證。一方面,雖然多版本控制機(jī)制提供了基本的CAS語(yǔ)義,但是在極端的高并發(fā)場(chǎng)景下仍因競(jìng)爭(zhēng)而存在性能問(wèn)題。另一方面,僅僅依靠多版本控制機(jī)制無(wú)法提供更加復(fù)雜的一致性語(yǔ)義,例如事務(wù)。盡管在數(shù)據(jù)副本層面,ZooKeeper和etcd都提供很強(qiáng)的一致性語(yǔ)義,但對(duì)于應(yīng)用層面的數(shù)據(jù)一致性卻還有很大的提升空間:ZooKeeper無(wú)法以原子的方式執(zhí)行一組相關(guān)操作,而etcd的事務(wù)僅支持有限的簡(jiǎn)單操作(簡(jiǎn)單邏輯判斷,簡(jiǎn)單狀態(tài)獲取,但不允許對(duì)同一個(gè)鍵進(jìn)行多次寫(xiě)操作)。

在應(yīng)用層數(shù)據(jù)層面,ZooKeeper和etcd都使用多版本控制機(jī)制提供最基礎(chǔ)的一致性保證。例如,ZooKeeper的所有寫(xiě)操作都支持樂(lè)觀并發(fā)控制:只有當(dāng)目標(biāo)節(jié)點(diǎn)的當(dāng)前版本與期望版本相同時(shí),寫(xiě)操作才允許成功;而etcd則更進(jìn)一步,還支持非常有限的簡(jiǎn)單事務(wù)操作。一方面,雖然多版本控制機(jī)制提供了基本的CAS語(yǔ)義,但是在極端的高并發(fā)場(chǎng)景下仍因競(jìng)爭(zhēng)而存在性能問(wèn)題。另一方面,僅僅依靠多版本控制機(jī)制無(wú)法提供更加復(fù)雜的一致性語(yǔ)義,例如事務(wù)。盡管在數(shù)據(jù)副本層面,ZooKeeper和etcd都提供很強(qiáng)的一致性語(yǔ)義,但對(duì)于應(yīng)用層面的數(shù)據(jù)一致性卻還有很大的提升空間:ZooKeeper無(wú)法以原子的方式執(zhí)行一組相關(guān)操作,尤其是同時(shí)操縱多個(gè)鍵;而etcd的事務(wù)僅支持非常有限的簡(jiǎn)單操作(簡(jiǎn)單邏輯判斷,簡(jiǎn)單狀態(tài)獲取,但不允許對(duì)同一個(gè)鍵進(jìn)行多次寫(xiě)操作)。為應(yīng)用層數(shù)據(jù)提供比現(xiàn)有的分布式存儲(chǔ)組件更強(qiáng)的一致性語(yǔ)義(復(fù)雜事務(wù))和更高的并發(fā)度是StateSynchronizer的主要目標(biāo),尤其是在stream原生場(chǎng)景下,因?yàn)閭鹘y(tǒng)的以隨機(jī)訪問(wèn)為主的存儲(chǔ)組件很難適配stream存儲(chǔ)的順序特性。得益于stream的自身特性,StateSynchronizer可以不依賴(lài)樂(lè)觀并發(fā)控制和CAS語(yǔ)義,這意味著不會(huì)出現(xiàn)版本沖突也無(wú)需重試,從而更加適用于高并發(fā)的場(chǎng)景(2.2.4小節(jié))。在“無(wú)條件寫(xiě)”模式下,StateSynchronizer的理論更新提交速度等價(jià)于stream的寫(xiě)入速度。

與現(xiàn)有的絕大多數(shù)存儲(chǔ)服務(wù)不同,StateSynchronizer反轉(zhuǎn)了傳統(tǒng)的數(shù)據(jù)存儲(chǔ)模型(2.2.3小節(jié)):它并不存儲(chǔ)共享狀態(tài)本身,轉(zhuǎn)而存儲(chǔ)所有作用在共享狀態(tài)上的更新操作。一方面,這一反轉(zhuǎn)的數(shù)據(jù)模型直接抽象出了共享狀態(tài),使得共享狀態(tài)不再局限于簡(jiǎn)單的鍵值存儲(chǔ),而可以推廣到任意需要一致性語(yǔ)義的狀態(tài)。另一方面,反轉(zhuǎn)數(shù)據(jù)存儲(chǔ)的同時(shí)還不可避免地反轉(zhuǎn)了數(shù)據(jù)相關(guān)的操作,使得原本大量的服務(wù)端狀態(tài)計(jì)算可以直接在客戶(hù)端本地完成(2.2.1小節(jié))。這一特性不僅大大降低了服務(wù)端的資源消耗,同時(shí)也使得StateSynchronizer可以提供更靈活的更新操作和更強(qiáng)一致性語(yǔ)義:復(fù)雜事務(wù)。在StateSynchronizer的框架中,客戶(hù)端提交的所有更新操作都是以原子的方式順序執(zhí)行的,并且所有更新操作的執(zhí)行都發(fā)生在本地。從邏輯上看,每一個(gè)更新操作都等價(jià)于一個(gè)本地事務(wù)操作。這也意味著客戶(hù)端可以在更新操作中使用復(fù)雜的業(yè)務(wù)邏輯(幾乎是不受限的操作,只要操作本身的作用是確定性的)而無(wú)需擔(dān)心一致性問(wèn)題。

2 實(shí)現(xiàn)細(xì)節(jié)

2.1\tStateSynchronizer的本質(zhì)

圖 1 StateSynchronizer的整體架構(gòu) [3] StateSynchronizer包括一個(gè)嵌入在應(yīng)用里的客戶(hù)端和一個(gè)用于“存儲(chǔ)”共享狀態(tài)的stream。

從整體架構(gòu)上看,StateSynchronizer是一個(gè)很典型的客戶(hù)端/服務(wù)器結(jié)構(gòu)(如圖 1所示):它包括一個(gè)以庫(kù)的形式(當(dāng)前版本僅支持Java)嵌入在應(yīng)用中的客戶(hù)端,以及服務(wù)器端的一個(gè)對(duì)應(yīng)stream。從概念上看,StateSynchronizer服務(wù)端負(fù)責(zé)以stream的形式“存儲(chǔ)”共享狀態(tài)。嚴(yán)格說(shuō)來(lái),stream存儲(chǔ)的是更新操作而不是共享狀態(tài)本身。2.2.3小節(jié)將對(duì)此進(jìn)行更加深入的討論。

StateSynchronizer客戶(hù)端是一個(gè)輕量級(jí)的組件,它與所有其它的stream客戶(hù)端(例如reader和writer)并沒(méi)有本質(zhì)上的不同:StateSynchronizer客戶(hù)端使用標(biāo)準(zhǔn)的stream API與服務(wù)器端的stream交互,并且服務(wù)器端也并不存在任何特定于StateSynchronizer的特性或?qū)崿F(xiàn)。也就是說(shuō),StateSynchronizer客戶(hù)端具有其它stream客戶(hù)端共同的優(yōu)點(diǎn),高效。所有StateSynchronizer特定的行為都是在客戶(hù)端實(shí)現(xiàn)的,服務(wù)器端僅僅用于提供stream形式的存儲(chǔ)媒介。StateSynchronizer的客戶(hù)端還非常精巧,核心部分的實(shí)現(xiàn)不過(guò)數(shù)百行代碼 [16]。

2.2\tStateSynchronizer的工作機(jī)制

2.2.1\t維護(hù)本地共享狀態(tài)

從概念上說(shuō),每一個(gè)StateSynchronizer都對(duì)應(yīng)一個(gè)共享狀態(tài):所有的客戶(hù)端都可以并發(fā)地對(duì)這個(gè)共享狀態(tài)進(jìn)行讀寫(xiě)操作,并且保持一致性。這個(gè)共享狀態(tài)既可以很簡(jiǎn)單(例如,它可以是一個(gè)基本的數(shù)值變量),也可以很復(fù)雜(例如,它也可以是一個(gè)任意復(fù)雜的數(shù)據(jù)結(jié)構(gòu))。但是,如果從物理實(shí)現(xiàn)角度上看,根本不存在這樣一個(gè)可以被共享訪問(wèn)的狀態(tài):每一個(gè)StateSynchronizer的客戶(hù)端都只在各自的本地維護(hù)著一個(gè)“共享”狀態(tài)的副本(Copy),除此以外沒(méi)有任何地方存儲(chǔ)這個(gè)狀態(tài)。所有的讀和寫(xiě)(更新)操作都是直接作用在這個(gè)本地共享狀態(tài)副本上:讀操作直接返回本地共享狀態(tài)副本,而更新操作作用于本地共享狀態(tài)并生成新的共享狀態(tài)。

為了達(dá)到順序一致性 [8],所有共享狀態(tài)必須滿(mǎn)足全序(Total Order)關(guān)系 [17]。如果用符號(hào)“?”表示二元happens-before語(yǔ)義 [18],則任意N個(gè)狀態(tài)必須能夠確定出唯一全局順序,如下:

(1)

注意,happens-before關(guān)系必須滿(mǎn)足傳遞性,反自反性和反對(duì)稱(chēng)性 [19]。

如果讀者閱讀過(guò)StateSynchronizer接口 [20]的實(shí)現(xiàn)類(lèi)StateSynchronizerImpl,就會(huì)發(fā)現(xiàn)它有一個(gè)名為currentState的StateT類(lèi)型的成員,并且StateT類(lèi)型實(shí)現(xiàn)了Revisioned接口。這就是StateSynchronizer所維護(hù)的本地共享狀態(tài)副本。Revisioned接口僅有兩個(gè)成員方法:getScopedStreamName()用于獲取該狀態(tài)對(duì)應(yīng)的stream的名字,getRevison()方法用于獲取該狀態(tài)對(duì)應(yīng)的Revision(一個(gè)抽象的版本概念,也可以近似等價(jià)為Kafka的offset)。而Revision接口最終繼承了Comparable接口,允許任意兩個(gè)Revision進(jìn)行比較,用于保證共享狀態(tài)的全序關(guān)系。感興趣的讀者可以繼續(xù)閱讀Revision接口的標(biāo)準(zhǔn)實(shí)現(xiàn)類(lèi)RevisionImpl的compareTo()方法,就會(huì)發(fā)現(xiàn)Revision的比較實(shí)際上是基于Segment偏移量進(jìn)行的。由于StateSynchronizer的底層stream僅包含一個(gè)segment,基于該segment的偏移量天然就是一個(gè)全序關(guān)系的良定義(well-defined)。

2.2.2\t更新操作的抽象模型

StateSynchronizer上的更新操作的實(shí)現(xiàn)是遞歸式的,也可以說(shuō)是生成式的。StateSynchronizer的客戶(hù)端接受一個(gè)更新操作un ,將其成功持久化后(細(xì)節(jié)將在下文討論)應(yīng)用于當(dāng)前的本地共享狀態(tài)副本sn,從而生成新?tīng)顟B(tài)sn+1 ,如下:

sn+1 = un(sn) (2)

從純數(shù)學(xué)的角度看,這是一個(gè)很典型的一階馬爾科夫模型/鏈(Markov Model) [21]:如果把n看作是離散的時(shí)間,那么sn就構(gòu)成了系統(tǒng)狀態(tài)隨時(shí)間遷移(Transition)的一個(gè)有序序列,并且該系統(tǒng)在任意時(shí)間點(diǎn)的狀態(tài)sn+1只依賴(lài)前一時(shí)刻的狀態(tài) sn ,并由當(dāng)前更新un 確定,而與任何其它狀態(tài)無(wú)關(guān)。也可以這么理解,我們假設(shè)了狀態(tài)sn 已經(jīng)包含了所有之前時(shí)刻的狀態(tài)信息。這就是所謂的馬爾科夫假設(shè)。為了啟動(dòng)狀態(tài)遷移,我們規(guī)定系統(tǒng)必須具有一個(gè)起始狀態(tài)s0 ,而更新操作引起了隨后的狀態(tài)遷移。

如果從集群的視角看,有多個(gè)StateSynchronizer客戶(hù)端獨(dú)立同時(shí)運(yùn)行并接受更新操作,而每個(gè)客戶(hù)端本地的共享狀態(tài)則分別經(jīng)歷著基于馬爾科夫模型的狀態(tài)遷移。為保證每個(gè)StateSynchronizer客戶(hù)端的本地共享狀態(tài)都能夠收斂于相同的最終狀態(tài),首先要求狀態(tài)遷移是確定性的(deterministic),也就是說(shuō),更新操作un 本身必須是確定性的(我們將在2.3.1小節(jié)深入討論更新操作與確定性問(wèn)題)。從這個(gè)角度看,上述馬爾可夫鏈其實(shí)已經(jīng)退化成一個(gè)普通狀態(tài)機(jī)。其次,所有的StateSynchronizer客戶(hù)端必須具有相同的起始狀態(tài)s0,并且以相同的順序應(yīng)用更新un。整個(gè)集群的這種行為模式非常類(lèi)似經(jīng)典的復(fù)制狀態(tài)機(jī)(Replicated State Machine)模型 [22]。復(fù)制狀態(tài)機(jī)模型是一個(gè)應(yīng)用廣泛的分布式模型,許多常見(jiàn)的全序廣播/原子廣播協(xié)議都是基于該模型進(jìn)行的,如ZAB協(xié)議和Raft協(xié)議等。我們有意忽略了著名的Paxos協(xié)議 [23] [24],因?yàn)樵腜axos協(xié)議并非用于解決全序廣播問(wèn)題,盡管共識(shí)算法與全序廣播之間確實(shí)被證明存在等價(jià)關(guān)系 [25]。復(fù)制狀態(tài)機(jī)模型可以簡(jiǎn)單描述如下:

  • 在各自獨(dú)立的服務(wù)器節(jié)點(diǎn)上放置同一狀態(tài)機(jī)的實(shí)例;

  • 接受客戶(hù)端請(qǐng)求,并轉(zhuǎn)譯成狀態(tài)機(jī)的輸入;

  • 確定輸入的順序;

  • 按已確定的順序在各個(gè)狀態(tài)機(jī)實(shí)例上執(zhí)行輸入;

  • 用狀態(tài)機(jī)的輸出回復(fù)客戶(hù)端;

  • 監(jiān)測(cè)各個(gè)狀態(tài)副本或者狀態(tài)機(jī)輸出可能出現(xiàn)的差異。

  • 復(fù)制狀態(tài)機(jī)最核心也是最困難的部分是如何確定出一個(gè)輸入順序,以便讓每個(gè)狀態(tài)機(jī)實(shí)例都嚴(yán)格按照該順序執(zhí)行狀態(tài)遷移,從而保證一致性。從整體架構(gòu)上來(lái)說(shuō),ZAB協(xié)議和Raft協(xié)議都依賴(lài)單一的主節(jié)點(diǎn)確定輸入順序:所有的更新操作只能通過(guò)主節(jié)點(diǎn)進(jìn)行,因此順序由主節(jié)點(diǎn)唯一確定。所不同的是,ZAB協(xié)議通過(guò)顯式的類(lèi)兩階段提交方法保持廣播更新操作的原子性,而Raft協(xié)議甚至沒(méi)有顯式的提交過(guò)程,直接依賴(lài)計(jì)數(shù)的方法實(shí)現(xiàn)隱式提交。

    在StateSynchronizer的場(chǎng)景下,狀態(tài)機(jī)實(shí)例即StateSynchronizer客戶(hù)端,輸入順序即更新操作的應(yīng)用順序,執(zhí)行狀態(tài)遷移即應(yīng)用更新操作至本地共享狀態(tài)。StateSynchronizer使用完全不同的方式解決輸入順序的確定問(wèn)題,使得StateSynchronizer不需要依賴(lài)任何主節(jié)點(diǎn)。從嚴(yán)格意義上說(shuō),StateSynchronizer并不負(fù)責(zé)維護(hù)數(shù)據(jù)副本,但是其本地共享狀態(tài)的維護(hù)和更新模型都與數(shù)據(jù)副本有著相似之處。我們將在下文詳細(xì)討論StateSynchronizer如何確定輸入順序以及和傳統(tǒng)模型的差別。

    如果讀者仔細(xì)閱讀過(guò)StateSynchronizer的源代碼,就會(huì)發(fā)現(xiàn)StateSynchronizer接口內(nèi)定義有一個(gè)名為UpdateGenerator的函數(shù)式接口。UpdateGenerator接口本質(zhì)上是一個(gè)二元消費(fèi)者:它接受兩個(gè)參數(shù),其中一個(gè)是StateT類(lèi)型的當(dāng)前共享狀態(tài),另一個(gè)是以List形式存在在更新操作(Update類(lèi)型)列表,而列表內(nèi) 的更新操作最終都將被持久化到相應(yīng)的stream上。從概念上看,UpdateGenerator接口其實(shí)就是公式 2的等價(jià)實(shí)現(xiàn)。

    2.2.3\t只存儲(chǔ)更新操作

    在傳統(tǒng)的數(shù)據(jù)庫(kù)模型中,數(shù)據(jù)庫(kù)的服務(wù)器端負(fù)責(zé)維護(hù)一個(gè)全局的持久化的共享狀態(tài),即數(shù)據(jù)庫(kù)中所有數(shù)據(jù)所組成的一個(gè)集合。多個(gè)獨(dú)立的客戶(hù)端同時(shí)向服務(wù)器端提交更新操作(事務(wù)),更新操作作用于共享狀態(tài)上引起狀態(tài)改變,而客戶(hù)端本地不存儲(chǔ)任何狀態(tài)。在這個(gè)模型中,服務(wù)器端的共享狀態(tài)無(wú)論從邏輯上看還是從物理上看,它都是共享的(這與StateSynchronizer的共享狀態(tài)有很大的不同):因?yàn)閹缀跛械臄?shù)據(jù)庫(kù)系統(tǒng)都允許多個(gè)事務(wù)并發(fā)執(zhí)行。從形式化的角度看,所謂“事務(wù)ui和uj是并發(fā)的”指的是它們既不滿(mǎn)足 ui ? uj 關(guān)系,也不滿(mǎn)足uj ? ui 關(guān)系,即ui 的作用對(duì)uj不完全可見(jiàn),并且uj的作用對(duì)ui也不完全可見(jiàn) [13]。可以不是很精確地將并發(fā)理解為:ui和uj之間無(wú)法確定順序。也可以從直覺(jué)上這樣理解:ui和uj的執(zhí)行,在時(shí)間上存在重疊部分。并發(fā)直接導(dǎo)致了數(shù)據(jù)一致性問(wèn)題。傳統(tǒng)數(shù)據(jù)庫(kù)模型解決并發(fā)問(wèn)題的手段是設(shè)置事務(wù)的隔離級(jí)別 [26]:并發(fā)事務(wù)在不同的隔離級(jí)別下有著不同的可見(jiàn)性。

    StateSynchronizer擯棄了傳統(tǒng)的數(shù)據(jù)庫(kù)模型,從一個(gè)完全不同的角度解決并發(fā)問(wèn)題和狀態(tài)機(jī)輸入順序問(wèn)題。其核心思想是,StateSynchronizer的服務(wù)器端只存儲(chǔ)(持久化)了更新操作本身而不是共享狀態(tài),共享狀態(tài)由每個(gè)客戶(hù)端獨(dú)立維護(hù),如2.2.1小節(jié)所述。由于StateSynchronizer架構(gòu)中并不存在物理上的共享狀態(tài),因此不會(huì)因?yàn)闋顟B(tài)共享而導(dǎo)致競(jìng)爭(zhēng),也不會(huì)因此產(chǎn)生并發(fā)問(wèn)題。對(duì)于每一個(gè)StateSynchronizer的客戶(hù)端而言,所有的更新操作都是順序地作用于本地的共享狀態(tài)副本(物理上順序執(zhí)行),這也不存在并發(fā)問(wèn)題。但是,單憑這一點(diǎn)還不足以保證共享狀態(tài)的一致性,除非能夠保證唯一的更新操作應(yīng)用順序。StateSynchronizer的服務(wù)器端用單segment的stream存儲(chǔ)了所有的更新操作:每一個(gè)更新操作作為一個(gè)event被持久化 ^5。Stream的最大特性就是只允許追加:所有的event寫(xiě)入操作只允許在尾部進(jìn)行(原子操作),并且一個(gè)event一旦寫(xiě)入就不允許修改。這一特性不僅使得多個(gè)writer可以同時(shí)進(jìn)行寫(xiě)入并且保持一致性,還使得所有event的順序得以唯一確定,即每個(gè)event最終在Segment內(nèi)的相對(duì)順序。所以,對(duì)于每一個(gè)StateSynchronizer客戶(hù)端來(lái)說(shuō),都能夠看見(jiàn)一個(gè)一致的有序的更新操作視圖。

    細(xì)心的讀者可能還希望進(jìn)一步了解服務(wù)器端的stream是如保持只允許追加的特性和一致性的。與Kafka的消息代理節(jié)點(diǎn)(Broker)直接用本地文件系統(tǒng)存儲(chǔ)stream數(shù)據(jù)的方法不同,Pravega的消息代理節(jié)點(diǎn)將數(shù)據(jù)的存儲(chǔ)完全交由一個(gè)抽象的存儲(chǔ)層代理,包括數(shù)據(jù)副本的維護(hù)。目前已經(jīng)支持的具體存儲(chǔ)層實(shí)現(xiàn)包括:BookKeeper [27],HDFS [28],Extended S3 [29],NFS [30]等等。也就是說(shuō),數(shù)據(jù)副本的實(shí)現(xiàn)對(duì)消息代理節(jié)點(diǎn)來(lái)說(shuō)是完全透明的。具體的segment分層存儲(chǔ)設(shè)計(jì)細(xì)節(jié)已經(jīng)超出本文的討論范圍,感興趣的讀者可以自行閱讀Pravega的相關(guān)文檔 [31]。

    StateSynchronizer的這種數(shù)據(jù)模型其實(shí)非常類(lèi)似Change Data Capture(CDC) [32]和Event Sourcing [33]的設(shè)計(jì)模式:不存儲(chǔ)系統(tǒng)狀態(tài),而是通過(guò)推導(dǎo)計(jì)算得出 [13]。以stream形式存在的更新操作其實(shí)可以看作是系統(tǒng)狀態(tài)的另一種視圖。從這一視圖出發(fā),不僅能夠推導(dǎo)出系統(tǒng)的最終狀態(tài),還可以得出系統(tǒng)在歷史任意時(shí)刻的狀態(tài)。

    為了讓所有的更新操作本身都能被持久化到stream中,StateSynchronizer要求所有的更新操作都以類(lèi)的形式實(shí)現(xiàn),封裝好所有所需的狀態(tài)并且支持序列化/反序列化。這一點(diǎn)從StateSynchronizer的接口定義上也可以反映出來(lái):創(chuàng)建一個(gè)StateSynchronizer實(shí)例必須提供兩個(gè)Serializer接口實(shí)例,分別用于對(duì)更新操作和起始狀態(tài)作序列化/反序列化,并且UpdateGenerator接口的定義要求所有更新操作必須實(shí)現(xiàn)Update接口。

    2.2.4\t更新操作的寫(xiě)入模式:條件寫(xiě)與無(wú)條件寫(xiě)

    將更新操作本身持久化到相應(yīng)的stream中是StateSynchronizer實(shí)現(xiàn)更新操作接口的重要步驟之一,因?yàn)橹挥羞@樣才能使所有的StateSynchronizer客戶(hù)端都看見(jiàn)一個(gè)全局唯一的更新操作序列。目前,StateSynchronizer支持以?xún)煞N不同的模式將更新操作持久化到stream端:條件寫(xiě)模式(Conditionally Write)與無(wú)條件寫(xiě)模式(Unconditionally Write)。這兩種更新模式分別有各自的適用場(chǎng)景。

    圖 2 條件寫(xiě)示意圖 每個(gè)矩形框代表已經(jīng)持久化到stream(右側(cè)為尾端)中的一個(gè)更新操作。實(shí)線框?yàn)橐呀?jīng)累積到當(dāng)前某個(gè)StateSynchronizer客戶(hù)端本地狀態(tài)的更新操作,而虛線框?yàn)樯形醋饔玫奖镜貭顟B(tài)的更新操作,即:其它StateSynchronizer客戶(hù)端提交但尚未被當(dāng)前StateSynchronizer客戶(hù)端拉取的更新操作。兩條豎線分隔符分別對(duì)應(yīng)當(dāng)前StateSynchronizer客戶(hù)端所見(jiàn)的Revision以及此時(shí)真正的最新Revision。只要存在虛線框所示的更新操作,或者說(shuō)只要當(dāng)前StateSynchronizer客戶(hù)端所見(jiàn)的Revision不是最新,那么條件寫(xiě)操作就無(wú)法成功完成。

    在條件寫(xiě)模式下(參考StateSynchronizer接口上updateState()方法的實(shí)現(xiàn)),當(dāng)StateSynchronizer客戶(hù)端嘗試把一個(gè)更新操作寫(xiě)入stream內(nèi)時(shí)需要首先檢查當(dāng)前本地的共享狀態(tài)是否是對(duì)應(yīng)stream上的最新?tīng)顟B(tài)。如果是,則寫(xiě)入成功,可以繼續(xù)將該更新操作作用于本地的共享狀態(tài)并更新為新?tīng)顟B(tài);如果不是,說(shuō)明已經(jīng)有其它的客戶(hù)端搶先往stream中寫(xiě)入了其它更新操作,此時(shí)本地的共享狀態(tài)已經(jīng)“過(guò)期”,本次寫(xiě)入失敗,如圖 2所示。對(duì)于寫(xiě)入失敗的情況,StateSynchronizer會(huì)自動(dòng)嘗試從stream拉取所有缺失的更新,并將所有拉取到的更新順序作用于當(dāng)前本地共享狀態(tài)以便將其更新到最新?tīng)顟B(tài),然后重試條件寫(xiě)。這一“失敗-重試”的過(guò)程可能重復(fù)多次,直至寫(xiě)入成功。從概念上看,條件寫(xiě)表現(xiàn)出的行為與多線程編程中的CAS操作有著諸多相似之處。

    如果讀者仔細(xì)思考條件寫(xiě)的實(shí)現(xiàn)細(xì)節(jié),不難得出如下的結(jié)論:檢查狀態(tài)是否過(guò)期與實(shí)際的stream寫(xiě)入動(dòng)作必須是一個(gè)整體的原子操作,否則將出現(xiàn)競(jìng)爭(zhēng)條件。事實(shí)上,檢查狀態(tài)是否過(guò)期這一動(dòng)作并不是在客戶(hù)端進(jìn)行的,而是由stream的相關(guān)接口直接代理的,否則很難與發(fā)生在服務(wù)器端的寫(xiě)入動(dòng)作合并為一個(gè)原子操作。在閱讀過(guò)StateSynchronizer實(shí)現(xiàn)類(lèi)StateSynchronizerImpl的源代碼之后,讀者會(huì)發(fā)現(xiàn)一個(gè)名為client的RevisionedStreamClient類(lèi)型成員。RevisionedStreamClient是StateSynchronizer客戶(hù)端用來(lái)與后端stream交互的唯一入口,所有stream的讀寫(xiě)操作都通過(guò)該接口進(jìn)行,包括條件寫(xiě)。RevisionedStreamClient接口上有一個(gè)名為writeConditionally()的方法(即條件寫(xiě)的真正實(shí)現(xiàn)),允許在寫(xiě)入一個(gè)event的同時(shí)指定一個(gè)Revision。正如其名字所暗示的那樣,Revision接口可以近似理解為stream的“版本”:每次成功的寫(xiě)入操作都會(huì)導(dǎo)致對(duì)應(yīng)stream的Revision發(fā)生變化,writeConditionally()方法甚至還直接返回該Revision以方便客戶(hù)端用作多版本并發(fā)控制。現(xiàn)在繼續(xù)討論writeConditionally()方法的行為,只有當(dāng)stream的當(dāng)前的實(shí)際Revision與指定的Revision相同時(shí)(即:從上次成功條件寫(xiě)入到目前為止都沒(méi)有其它的成功寫(xiě)入發(fā)生),真正的寫(xiě)入動(dòng)作才發(fā)生,否則寫(xiě)入失敗。很明顯,這是一個(gè)典型的樂(lè)觀并發(fā)控制模式。

    聰明的讀者甚至還可以從物理實(shí)現(xiàn)角度理解Revision。從2.2.1小節(jié)的討論中我們知道,Revision是基于segment內(nèi)的偏移量實(shí)現(xiàn)的,而segment本質(zhì)上就是一個(gè)無(wú)邊界的字節(jié)流。所謂stream的“版本”其實(shí)就是stream當(dāng)前尾端的偏移量。由于stream只允許追加的特性,往指定偏移位置執(zhí)行寫(xiě)入操作時(shí),只有當(dāng)該偏移確實(shí)處于尾端時(shí)才能成功。圖 2中所標(biāo)記的Revision既可以看作是當(dāng)前本地共享狀態(tài)所對(duì)應(yīng)的stream版本,也可以看作是當(dāng)前StateSynchronizer客戶(hù)端所看見(jiàn)的stream尾部位置。從這個(gè)角度看,stream的特性和操作得到了統(tǒng)一。

    由于條件寫(xiě)的失敗-重試機(jī)制,在某些極端場(chǎng)景下(例如更新操作極度頻繁引起的激烈競(jìng)爭(zhēng)),可能導(dǎo)致較多次數(shù)的重試。并且由于條件寫(xiě)操作目前并未實(shí)現(xiàn)公平機(jī)制,理論上可能出現(xiàn)某個(gè)客戶(hù)端“饑餓”的情況。為應(yīng)對(duì)這種場(chǎng)景,StateSynchronizer還提供了另一種持久化模式:無(wú)條件寫(xiě)模式。在無(wú)條件寫(xiě)模式下(參考StateSynchronizer接口上updateStateUnconditionally()方法的實(shí)現(xiàn)),StateSynchronizer客戶(hù)端往stream寫(xiě)入更新操作時(shí)并不會(huì)要求比較Revision,而是無(wú)條件地將該更新操作寫(xiě)入當(dāng)前stream的實(shí)際尾端,并且在寫(xiě)入成功后也不會(huì)更新本地的共享狀態(tài)。從實(shí)現(xiàn)上看,無(wú)條件寫(xiě)模式下的更新動(dòng)作其實(shí)就是一個(gè)簡(jiǎn)單的stream追加動(dòng)作。在服務(wù)和資源正常的情況下,stream的追加寫(xiě)入總是能夠成功的。如果調(diào)用者希望得到更新操作作用后的共享狀態(tài),則還需要手動(dòng)拉取一次更新(參考StateSynchronizer接口上的fetchUpdates()方法)。由于更新操作的件寫(xiě)入動(dòng)作與拉取動(dòng)作之間存在時(shí)間窗口,在這段時(shí)間內(nèi)可能已經(jīng)有其它的客戶(hù)端繼續(xù)寫(xiě)入新的更新操作。因此,在拉取得到的更新操作序列上,并不能保證之前提交的更新操作是該序列上的最后一個(gè)元素。也就是說(shuō),在應(yīng)用該更新操作之前和之后,可能有其它的更新操作已經(jīng)作用或繼續(xù)作用在當(dāng)前本地共享狀態(tài)上。相反,條件寫(xiě)模式卻總是能保證所提交的更新一定是最后一個(gè)作用在當(dāng)前本地共享狀態(tài)上的操作。根據(jù)具體應(yīng)用場(chǎng)景的不同,這可能是個(gè)問(wèn)題,也可能不是。例如,在無(wú)條件寫(xiě)模式下,所有的更新操作現(xiàn)在都變得不可觀測(cè)了:假設(shè)你執(zhí)行了一個(gè)無(wú)條件的更新操作,往一個(gè)共享的集合里面添加了一個(gè)元素。現(xiàn)在,哪怕你立刻進(jìn)行集合遍歷,也不能保證你一定能夠找到剛剛添加的元素,因?yàn)榭赡艽嬖谄渌蛻?hù)端提交的后續(xù)更新操作已經(jīng)將剛剛添加的元素刪除了。這恐怕是一種與直覺(jué)相違背的行為表現(xiàn)。總之,與條件寫(xiě)相比,無(wú)條件寫(xiě)有著優(yōu)異的并發(fā)性能,但是這一切都是有代價(jià)的,例如:犧牲了開(kāi)發(fā)者的可理解性。

    2.3\t其它問(wèn)題

    2.3.1\t更新操作與確定性

    StateSynchronizer的更新操作模型(2.2.2小節(jié))要求所有更新操作的實(shí)現(xiàn)必需是確定性的,因?yàn)樗械母虏僮鞫紩?huì)在每一個(gè)StateSynchronizer客戶(hù)端被重放。對(duì)于相同的輸入,如果更新操作本身不能夠產(chǎn)生確定性的結(jié)果,即使以完全相同的順序在每一個(gè)客戶(hù)端被執(zhí)行,也會(huì)破壞共享狀態(tài)的最終一致性。根據(jù)實(shí)際業(yè)務(wù)場(chǎng)景的不同,這一要求可能是一個(gè)問(wèn)題,也可能不是,例如:

    • 不可以使用隨機(jī)函數(shù)。這一看似簡(jiǎn)單的要求實(shí)際上限制了不少可能性,很多科學(xué)計(jì)算依賴(lài)隨機(jī)函數(shù)。

    • 不可以使用絕大多數(shù)的本地狀態(tài),例如:本地時(shí)間,本機(jī)硬件信息等。

    • 引用任何外部系統(tǒng)的狀態(tài)都需要格外小心可能引入的不一致。例如,如果一個(gè)外部系統(tǒng)的狀態(tài)會(huì)隨時(shí)間變化,各個(gè)客戶(hù)端可能看到各不相同的外部狀態(tài),因?yàn)橥粋€(gè)更新操作在每個(gè)客戶(hù)端被執(zhí)行的時(shí)間點(diǎn)是不確定的。

    除了保證更新操作的確定性之外,還需要特別注意更新操作的執(zhí)行是否具有“副作用”,例如:引發(fā)全局狀態(tài)或外部系統(tǒng)狀態(tài)的改變。如果回答是肯定的,那么還需要特別注意這些引發(fā)狀態(tài)改變的動(dòng)作接口是否具有冪等性 [34],因?yàn)橥粋€(gè)更新操作不僅會(huì)在每個(gè)客戶(hù)端被執(zhí)行,即使在同一客戶(hù)端也可能被執(zhí)行多次(2.2.4小節(jié))。

    2.3.2\t更新操作與更新丟失問(wèn)題

    有人擔(dān)心StateSynchronizer是否存在丟失更新問(wèn)題 [6]。丟失更新問(wèn)題一般在如下場(chǎng)景發(fā)生:兩個(gè)進(jìn)程并發(fā)地對(duì)同一共享變量進(jìn)行“讀取-修改-寫(xiě)入”組合操作。如果這一組合操作不能夠被作為一個(gè)原子操作完成,那么后寫(xiě)入的狀態(tài)有可能覆蓋另一個(gè)寫(xiě)入操作的結(jié)果,導(dǎo)致其中一個(gè)修改結(jié)果(更新)“丟失”。如2.2.3小節(jié)所述,所有的更新操作都是在StateSynchronizer的客戶(hù)端本地順序執(zhí)行的,因此不存在并發(fā)修改共享狀態(tài)的場(chǎng)景,也不會(huì)產(chǎn)生更新丟失問(wèn)題。

    雖然StateSynchronizer客戶(hù)端保證了以并發(fā)安全的方式執(zhí)行所有更新操作,但是,一個(gè)不正確實(shí)現(xiàn)的更新操作仍有可能導(dǎo)致更新丟失問(wèn)題。如果一個(gè)應(yīng)用需要實(shí)現(xiàn)“讀取-修改-寫(xiě)入”組合操作,唯一正確的做法是將所有的讀取,修改和寫(xiě)入動(dòng)作都封裝在同一個(gè)更新操作中,即按如下偽代碼所示實(shí)現(xiàn)更新操作un


    un

    \u0026gt; 讀取狀態(tài)sn

    \u0026gt; 執(zhí)行修改;

    \u0026gt; 生成并返回新?tīng)顟B(tài)sn+1


    源代碼 1 用偽代碼表示的更新操作一般實(shí)現(xiàn)

    一種常見(jiàn)的錯(cuò)誤是在更新操作un外部進(jìn)行“讀取狀態(tài)sn”和“執(zhí)行修改”動(dòng)作,并將新?tīng)顟B(tài)sn+1直接封裝進(jìn)更新操作un。另一種不那么直觀的錯(cuò)誤是,盡管將“讀取”,“修改”和“寫(xiě)入”動(dòng)作都封裝進(jìn)了同一個(gè)更新操作,但是在進(jìn)行“讀取狀態(tài)sn”動(dòng)作時(shí)有意或無(wú)意地使用了某種緩存機(jī)制,即并非每次都從StateSynchronizer獲取當(dāng)前共享狀態(tài)sn。這兩種錯(cuò)誤的實(shí)現(xiàn)都將導(dǎo)致很?chē)?yán)重的丟失更新問(wèn)題。2.2.4小節(jié)的相關(guān)討論解釋了其中的原因:由于條件寫(xiě)操作可能失敗并重試多次,并且每次重試都意味著StateSynchronizer客戶(hù)端本地的共享狀態(tài)已經(jīng)改變,任何緩存或者等價(jià)的行為都將導(dǎo)致實(shí)際的“執(zhí)行修改”動(dòng)作作用在一個(gè)已經(jīng)過(guò)期的舊狀態(tài)上,從而導(dǎo)致丟失更新問(wèn)題。

    2.3.3\t更新操作的順序執(zhí)行與性能

    在每一個(gè)StateSynchronizer客戶(hù)端上,所有的更新操作都是順序執(zhí)行并作用在本地共享狀態(tài)上的,正所謂“解決并發(fā)問(wèn)題最簡(jiǎn)單的辦法就是完全消除并發(fā)” [13]。有人擔(dān)心更新操作的順序執(zhí)行是否會(huì)顯著降低系統(tǒng)性能。從目前已有的研究看,用單線程的方式執(zhí)行所有事務(wù)是完全可行的 [35],并且在很多現(xiàn)有的數(shù)據(jù)庫(kù)實(shí)現(xiàn)中已經(jīng)被采用,例如:VoltDB/H-Store [36],Redis [37],Datomic [38] [39]等。當(dāng)然,這對(duì)事務(wù)本身以及數(shù)據(jù)集都有所要求 [13],例如:

    • 每個(gè)事務(wù)必須足夠小,并且足夠快。

    • 數(shù)據(jù)集的活躍部分必須足夠小,以便能夠全部載入物理內(nèi)存。否則,頁(yè)面的頻繁換入和換出會(huì)引起大量的磁盤(pán)IO操作,導(dǎo)致事務(wù)頻繁阻塞。

    • 寫(xiě)操作的吞吐量必須足夠小,以便單CPU核心可以有足夠的能力處理。否則,CPU運(yùn)算能力將成為瓶頸。

    對(duì)于一個(gè)StateSynchronizer應(yīng)用來(lái)說(shuō),無(wú)論是共享狀態(tài)還是更新操作的設(shè)計(jì)實(shí)現(xiàn),都必須遵循上述要求。

    2.3.4\t歷史重放與狀態(tài)壓縮

    每一個(gè)StateSynchronizer客戶(hù)端在進(jìn)行啟動(dòng)后的首次更新操作時(shí),都需要從對(duì)應(yīng)的stream拉取所有的歷史更新操作,并重放這些操作以便得到當(dāng)前最新的共享狀態(tài)。如果這是一個(gè)長(zhǎng)時(shí)運(yùn)行的共享狀態(tài),那么stream內(nèi)此時(shí)可能已經(jīng)累積了相當(dāng)數(shù)量的更新操作。拉取并重放所有這些更新操作可能需要消耗大量的時(shí)間與資源,造成首次更新性能低下。為了應(yīng)對(duì)這種場(chǎng)景,StateSynchronizer還提供了所謂的狀態(tài)壓縮機(jī)制。狀態(tài)壓縮(compact)是一個(gè)特殊的StateSynchronizer接口方法,它允許將StateSynchronizer客戶(hù)端的本地共享狀作為一個(gè)新的起始狀態(tài),用條件寫(xiě)模式重新寫(xiě)入stream^6,并且使用stream的mark機(jī)制標(biāo)記該起始狀態(tài)的最新位置^7。StateSynchronizer客戶(hù)端每次拉取更新操作時(shí),都會(huì)首先嘗試使用mark機(jī)制定位到最新的起始狀態(tài)并忽略所有之前的更新操作,從而避免了長(zhǎng)時(shí)間的歷史重放。

    如果首次更新操作的性能對(duì)于應(yīng)用程序來(lái)說(shuō)非常重要,那么開(kāi)發(fā)者可以選擇周期性地進(jìn)行狀態(tài)壓縮。那么首次更新操作所要拉取和應(yīng)用的更新操作數(shù)量則不會(huì)多于一個(gè)周期內(nèi)所累積的更新操作數(shù)量,這將大大提升首次更新操作的性能。

    3\t總結(jié)

    本文主要從狀態(tài)共享和一致性的角度出發(fā),詳細(xì)描述了Pravega的狀態(tài)同步組件StateSynchronizer的工作機(jī)制和實(shí)現(xiàn)細(xì)節(jié)。StateSynchronizer支持分布式環(huán)境下的多進(jìn)程同時(shí)讀寫(xiě)共享狀態(tài),并提供一致性保證。StateSynchronizer具有典型的客戶(hù)端/服務(wù)器架構(gòu),但是卻非常輕量和高效,因?yàn)榉?wù)器端僅僅用于提供存儲(chǔ)媒介。StateSynchronizer的核心工作機(jī)制可以歸納為兩個(gè)關(guān)鍵點(diǎn):維護(hù)本地共享狀態(tài)和只存儲(chǔ)更新操作本身。StateSynchronizer利用stream的天然特性實(shí)現(xiàn)了更新操作的全局有序。StateSynchronizer還提供了條件寫(xiě)和無(wú)條件寫(xiě)兩種更新寫(xiě)入模式,可以適用于并發(fā)度極高的場(chǎng)景。StateSynchronizer未來(lái)的工作可能集中在如何向開(kāi)發(fā)者提供更加便捷易用的編程接口,以減輕開(kāi)發(fā)者的負(fù)擔(dān)。

    Pravega系列文章計(jì)劃

    Pravega根據(jù)Apache 2.0許可證開(kāi)源,0.4版本已于近日發(fā)布。我們歡迎對(duì)流式存儲(chǔ)感興趣的大咖們加入Pravega社區(qū),與Pravega共同成長(zhǎng)。本篇文章為Pravega系列第五篇,系列文章如下:

  • 實(shí)時(shí)流處理(Streaming)統(tǒng)一批處理(Batch)的最后一塊拼圖:Pravega

  • 開(kāi)源Pravega架構(gòu)解析:如何通過(guò)分層解決流存儲(chǔ)的三大挑戰(zhàn)?

  • Pravega應(yīng)用實(shí)戰(zhàn):為什么云原生特性對(duì)流存儲(chǔ)至關(guān)重要

  • “ToB” 產(chǎn)品必備特性: Pravega的動(dòng)態(tài)彈性伸縮

  • 高并發(fā)下新的分布式一致性解決方案(StateSynchronizer)

  • Pravega的僅一次語(yǔ)義及事務(wù)支持

  • 與Apache Flink集成使用

  • 作者簡(jiǎn)介

    • 蔡超前:華東理工大學(xué)計(jì)算機(jī)應(yīng)用專(zhuān)業(yè)博士研究生,現(xiàn)就職于Dell EMC,6年搜索和分布式系統(tǒng)開(kāi)發(fā)以及架構(gòu)設(shè)計(jì)經(jīng)驗(yàn),現(xiàn)從事流相關(guān)的設(shè)計(jì)與研發(fā)工作。

    • 滕昱:現(xiàn)就職于Dell EMC非結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)部門(mén) (Unstructured Data Storage)團(tuán)隊(duì)并擔(dān)任軟件開(kāi)發(fā)總監(jiān)。2007年加入Dell EMC以后一直專(zhuān)注于分布式存儲(chǔ)領(lǐng)域。參加并領(lǐng)導(dǎo)了中國(guó)研發(fā)團(tuán)隊(duì)參與兩代Dell EMC對(duì)象存儲(chǔ)產(chǎn)品的研發(fā)工作并取得商業(yè)上成功。從 2017年開(kāi)始,兼任Streaming存儲(chǔ)和實(shí)時(shí)計(jì)算系統(tǒng)的設(shè)計(jì)開(kāi)發(fā)與領(lǐng)導(dǎo)工作。

    參考文獻(xiàn)

    [1] \t“Pravega,” Dell EMC, [Online]. Available: https://github.com/pravega/pravega.

    [2] \t“Working with Pravega: State Synchronizer,” Dell EMC, [Online]. Available: https://github.com/pravega/pravega/blob/master/documentation/src/docs/state-synchronizer.md.

    [3] \t“Pravega Concepts,” Dell EMC, [Online]. Available: https://github.com/pravega/pravega/blob/master/documentation/src/docs/pravega-concepts.md.

    [4] \tH. T. Kung and J. T. Robinson, “On optimistic methods for concurrency control,” ACM Transactions on Database Systems, vol. 6, no. 2, pp. 213-226, 1981.

    [5] \tP. A. Bernstein and N. Goodman, “Concurrency Control in Distributed Database Systems,” ACM Computing Surveys, vol. 13, no. 2, pp. 185-221, 1981.

    [6] \t“Concurrency Control,” Wikipedia, [Online]. Available: https://en.wikipedia.org/wiki/Concurrency_control.

    [7] \t“Multiversion Concurrency Control,” Wikipedia, [Online]. Available: https://en.wikipedia.org/wiki/Multiversion_concurrency_control.

    [8] \tP. Viotti and M. Vukoli?, “Consistency in Non-Transactional Distributed Storage Systems,” ACM Computing Surveys (CSUR), vol. 49, no. 1, 2016.

    [9] \tP. Bailis, A. Davidson, A. Fekete, A. Ghodsi, J. M. Hellerstein and I. Stoica, “Highly available transactions: virtues and limitations,” in Proceedings of the VLDB Endowment, 2013.

    [10] \tM. P. Herlihy and J. M. Wing, “Linearizability: a correctness condition for concurrent objects,” ACM Transactions on Programming Languages and Systems (TOPLAS) , vol. 12, no. 3, pp. 463-492, 1990 .

    [11] \t“Apache ZooKeeper,” [Online]. Available: https://zookeeper.apache.org/.

    [12] \t“etcd (GitHub Repository),” [Online]. Available: https://github.com/etcd-io/etcd.

    [13] \tM. Kleppmann, Designing Data-Intensive Applications, O’Reilly Media, 2017.

    [14] \tF. P. Junqueira, B. C. Reed and M. Sera?ni, “Zab: High-performance broadcast for primary-backup systems,” In DSN, pp. 245-256, 2011.

    [15] \tD. Ongaro and J. Ousterhout, “In search of an understandable consensus algorithm,” in Proceedings of the 2014 USENIX conference on USENIX Annual Technical Conference, Philadelphia, 2014.

    [16] \t“StateSynchronizer Related Source Code in Pravega GitHub Repository,” Dell EMC, [Online]. Available: https://github.com/pravega/pravega/tree/master/client/src/main/java/io/pravega/client/state.

    [17] \tM. Hazewinkel, Ed., Encyclopaedia of Mathematics (set), 1 ed., Springer Netherlands, 1994.

    [18] \tL. Lamport, “Time, clocks, and the ordering of events in a distributed system,” Communications of the ACM, vol. 21, no. 7, pp. 558-565, 1978.

    [19] \t“Happened-before,” Wikipedia, [Online]. Available: https://en.wikipedia.org/wiki/Happened-before.

    [20] \t“StateSynchronizer Interface Definition (v0.4),” Dell EMC, [Online]. Available: https://github.com/pravega/pravega/blob/r0.4/client/src/main/java/io/pravega/client/state/StateSynchronizer.java.

    [21] \tP. A. Gagniuc, Markov Chains: From Theory to Implementation and Experimentation, New Jersey: John Wiley \u0026amp; Sons, 2017.

    [22] \tF. B. Schneider, “Implementing fault-tolerant services using the state machine approach: a tutorial,” ACM Computing Surveys, vol. 22, no. 4, pp. 299-319, 1990.

    [23] \tL. Lamport, “The part-time parliament,” ACM Transactions on Computer Systems, vol. 16, no. 2, pp. 133-169, 1998.

    [24] \tL. Lamport, “Paxos Made Simple,” SIGACT News, vol. 32, no. 4, pp. 51-58, 2001.

    [25] \tX. Défago, A. Schiper and P. Urbán, “Total order broadcast and multicast algorithms: Taxonomy and survey,” ACM Computing Surveys, vol. 36, no. 4, pp. 372-421, 2004.

    [26] \tH. Berenson, P. Bernstein, J. Gray, J. Melton, E. O’Neil and P. O’Neil, “A critique of ANSI SQL isolation levels,” in Proceedings of the 1995 ACM SIGMOD international conference on Management of data, San Jose, California, USA, 1995.

    [27] \t“Apache BookKeeper,” [Online]. Available: https://bookkeeper.apache.org/.

    [28] \t“Apache Hadoop,” [Online]. Available: https://hadoop.apache.org/.

    [29] \t“Amazon S3,” Amazon, [Online]. Available: https://aws.amazon.com/s3/.

    [30] \t“NFS version 4.2 (RFC 7862),” [Online]. Available: https://tools.ietf.org/html/rfc7862.

    [31] \t“Pravega Segment Store Service (v0.4),” Dell EMC, [Online]. Available: https://github.com/pravega/pravega/blob/r0.4/documentation/src/docs/segment-store-service.md.

    [32] \t“Change Data Capture,” Wikipedia, [Online]. Available: https://en.wikipedia.org/wiki/Change_data_capture.

    [33] \tM. Fowler, “Event Sourcing,” 12 12 2005. [Online]. Available: https://martinfowler.com/eaaDev/EventSourcing.html.

    [34] \t“Idempotence,” Wikipedia, [Online]. Available: https://en.wikipedia.org/wiki/Idempotence.

    [35] \tM. Stonebraker, S. Madden and D. J. Abadi, “The End of an Architectural Era (It’s Time for a Complete Rewrite),” in Proceedings of the 33rd international conference on Very large data bases, Vienna, 2007.

    [36] \tR. Kallman, H. Kimura and J. Natkins, “H-Store: A High-Performance, Distributed Main Memory Transaction Processing System,” Proceedings of the VLDB Endowment, vol. 1, no. 2, pp. 1496-1499, 2008.

    [37] \t“Redis,” [Online]. Available: https://redis.io/.

    [38] \tR. Hickey, “The Architecture of Datomic,” 2 11 2012. [Online]. Available: https://www.infoq.com/articles/Architecture-Datomic.

    [39] \t“Datomic Cloud,” Cognitect, Inc., [Online]. Available: https://www.datomic.com/.

    更多內(nèi)容,請(qǐng)關(guān)注AI前線

    總結(jié)

    以上是生活随笔為你收集整理的取代ZooKeeper!高并发下的分布式一致性开源组件StateSynchronizer的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。