分布式存儲的七方面探討

在存儲層,數(shù)據(jù)采用列式存儲,可獲得很好的編碼效率,降低讀放大和空間放大,訪問持久化存儲的帶寬被高效利用,CPU和Memory的帶寬增速相對于持久化存儲帶寬增速表現(xiàn)出了顯著的差異,使用CPU的計算交換磁盤帶寬,提升了數(shù)據(jù)的處理能力。

360截圖16410119186063.png

存儲領域內(nèi)的很多知識,可以歸結(jié)于7個方面:復制、存儲引擎、事務、分析、多核、計算和編譯。

什么是分布式存儲呢?如果一個存儲系統(tǒng),不管是對象、塊、文件、kv、log、olap、oltp,只要對所管理的數(shù)據(jù)做了Partitioning&Replication,不管姿勢對不對,其實都可以歸納于分布式存儲。分布式存儲就是:Partitioning以多機scale,Replication以災備容錯。

1.復制

復制是解決可用性,可擴展性和高性能的關鍵。為了災備,數(shù)據(jù)需要冗余存儲;為了高可用,服務需要hot standby。缺乏災備的系統(tǒng)難以在生產(chǎn)環(huán)境使用。元數(shù)據(jù)和數(shù)據(jù)的維護均離不開復制,復制可轉(zhuǎn)移而不可消除。復制引出了多副本一致性問題,而一致性保證需要考慮各種軟件和硬件故障,以及誤操作。共識算法和復制日志是解決復制問題的核心,具體涉及:

故障檢查,租約協(xié)議;

選主算法,primary uniqueness invariant,網(wǎng)絡隔離,腦裂,雙主,拜占庭故障,failfast/failstop,failover;

日志復制,RSM;

membership change,或者config change,主機上下線管理,擴縮容;

數(shù)據(jù)復制,data rebalancing,data recovery;

副本放置邏輯和副本路由邏輯;

primary和backup之間如何fence?

外部一致性,線性一致性;

pipeline,fanout,primary-backup,quorum-based,gossip;

分布式日志,active-standby架構(gòu),active-active架構(gòu);

……

2.存儲引擎

此處的引擎是指本地的持久化存儲引擎,持久化存儲引擎要考慮CPU和memory系統(tǒng)和持久化設備之間的速度和帶寬差異,可以總結(jié)為1-3-5。

1是指fsync的調(diào)用,以及和fsync地位等同的函數(shù)的調(diào)用。調(diào)用次數(shù),調(diào)用頻率,在多少數(shù)據(jù)上施加調(diào)用等。設備的帶寬和利用率是否合理,fsync的調(diào)用怎么均攤到更多io次數(shù)和吞吐之上呢?

3是指讀/寫/空間的放大。怎么tradeoff,犧牲一個保住其他兩個呢?

5是指WAL的5個LSN,分別為prepare point,commit point,apply point,checkpoint,prune point。

處于prepare point和commit point之間的數(shù)據(jù)需要group commit;

處于apply point和commit point點之間的提交數(shù)據(jù)需要apply到內(nèi)存的數(shù)據(jù)結(jié)構(gòu)之上以后對讀可見;

內(nèi)存數(shù)據(jù)結(jié)構(gòu)需要以一定的調(diào)度策略存檔于持久化設備,checkpoint就是存檔點;

recover from crash需要從最近的checkpoint點恢復到commit point;

而一段checkpoint完成,則截止該checkpoint的日志可以截斷,因此存在一個不大于checkpoint的點,是為prune point,截止改點的日志和老的on-disk鏡像可以安全刪除。

因此這5個點始終保持一條約束:它們之間存在全序關系。

數(shù)據(jù)結(jié)構(gòu)和算法

內(nèi)存和磁盤數(shù)據(jù)的管理,需要用到豐富的數(shù)據(jù)結(jié)構(gòu)和算法。此外解壓縮和編解碼算法用于降低數(shù)據(jù)的size,也很有意思。

3.事務

事務是指ACID,很多存儲系統(tǒng),其實可以用事務做baseline進行分析,看這個系統(tǒng)在原子性,一致性,隔離性和持久化四個方面的設計究竟走多遠。其實事務反應了正確性和并發(fā)性的折中。作為事務的使用方,理論上(實踐上未必),并發(fā)訪問存儲系統(tǒng)時,不需要擔心結(jié)果不正確的問題。假如這種擔憂存在,一定是事務處理上,犧牲了某些正確性,偏向了并發(fā)性,將錯誤處理和選擇權交給了使用方處理,使用方需要額外花費心智在客戶代碼中插入fence代碼和做容錯處理。

存儲引擎部分,其實已經(jīng)或多或少地涉及到了事務處理的提交協(xié)議,提交協(xié)議主要解決事務的A和D。事務處理的核心是并發(fā)控制(concurrency control)協(xié)議。并發(fā)控制主要解決這樣的問題:

兩個并發(fā)事務沖突(讀寫,寫讀,寫寫),應該怎么處理呢?加鎖等待還是檢測到?jīng)_突主動夭折呢?

已經(jīng)提交的事務對數(shù)據(jù)庫的影響,怎么對當前outstanding事務的讀操作可見呢?

這兩個問題本質(zhì)是隔離性和一致性,沖突事務加以同步,前置的提交事務對后置outstanding事務可見。

理想的正確性是這樣子的:所有的事務,不管是否存在沖突,都一個一個串行執(zhí)行,時間上沒有任何重疊。理想的并發(fā)性是這樣子的:所有事務都沒有沖突,可以以最佳的并行度同時執(zhí)行。但現(xiàn)實是沖突始終存在,解決沖突,意味著在并行執(zhí)行的某個環(huán)節(jié)插入了同步點,需要判斷和仲裁是否有沖突。

沖突等待,是lock-based CC協(xié)議;沖突夭折重試,是timestamp ordering CC協(xié)議。前者就是所謂的悲觀事務,后者則為樂觀事務。事務處理在Jim Gray的書中和知名教材里其實已經(jīng)講得很清楚了。這里只提一下樂觀事務的沖突檢查的直觀性的簡單的理解:兩個事務存在兩種定序方法,一種是由TSO分配的時間戳確定的順序,一種是由數(shù)據(jù)依賴(WAW,RAW,WAR)確定順序。如果這兩種順序破壞事務之間的偏序關系,那么這兩個事務必然沖突,可以選擇讓一個事務夭折并且重試。

定序是一個比較關鍵的問題,比如樂觀事務的begin和commit的時間戳分配,悲觀事務的基于時間戳的死鎖預防也會用到時間戳的分配。

存儲系統(tǒng)自身做了partitioning之后,單個partition的事務處理可下沉于本地存儲引擎,然而如果一個操作涉及對多個partition的修改,則需要考慮跨partition的事務處理能力。其實分布式事務并沒有一個清晰的定義,但蘊含了“跨(across)”的意思,不管是提交協(xié)議還是CC協(xié)議,都依賴于分布式存儲系統(tǒng)的實現(xiàn)或者單機事務的實現(xiàn)。雖然有很多文獻中反復用到2PC和3PC,但有時候是指提交協(xié)議,有時候是指CC協(xié)議,有時候是指提交協(xié)議和CC協(xié)議的混合體。

事務給存儲系統(tǒng)的行為做出了明確的定義和保證,從事務的角度可推測系統(tǒng)的實現(xiàn),比如加鎖的粒度,多版本的管理,全局同步點,怎么處理write-too-late問題。

4.分析

分析處理涵蓋的東西太多了。從一個角度看,是怎么實現(xiàn)SQL語言;從另一個角度看,是怎么實現(xiàn)一個分布式系統(tǒng)由SQL驅(qū)動起來工作。一條文本的SQL語句,歷經(jīng)分詞,語法分析,訪問catalog和語意分析,關系代數(shù)的等價變化,形成邏輯的查詢計劃,然后根據(jù)數(shù)據(jù)的分布,算子自身特點和計算資源狀況,生成物理執(zhí)行計劃。

一條SQL可以對應多個邏輯執(zhí)行計劃,一個邏輯執(zhí)行計劃,對應多個物理執(zhí)行計劃。比如join的順序,join的算子的實現(xiàn)。謂詞過濾時謂詞的順序,謂詞是否下沉。一個關系代數(shù)的算子,有多種的不同實現(xiàn)算法,而多個關系算子,不同的計算順序也會有不同的執(zhí)行效率。根據(jù)先驗知識,使用啟發(fā)式的策略;或者利用數(shù)據(jù)分布的統(tǒng)計信息,采用某種cost估算模型;又或者根據(jù)已有執(zhí)行經(jīng)驗,自適應地調(diào)整到最優(yōu)或者次優(yōu)的執(zhí)行計劃。

在計算層,通過三大優(yōu)化策略parallelism,pipelining和batch加速處理。比如數(shù)據(jù)經(jīng)過parititioning形成多個partition,放置于多機上,采用MPP的方式,做多機的并行處理。計算的過程可以看成是把關系作為輸入,流經(jīng)執(zhí)行樹的葉子節(jié)點,匯聚于根節(jié)點,每個節(jié)點的對應算子的具體算法實現(xiàn)所輸入數(shù)據(jù)進行處理后輸出。從計算模型的角度看,有這么幾種情況:

tuple-at-a-time:采用了迭代器的模式,當前迭代器執(zhí)行get_next時,調(diào)用child算子對應迭代器的get_next獲取計算所需的輸入tuple,然后執(zhí)行一段計算代碼,產(chǎn)生一個輸出tuple,發(fā)射parent算子。

full materialization:每個算子接收到全部的輸入數(shù)據(jù),計算出輸出結(jié)果,交給下一個算子計算。這種方式類似于批處理。

vectorized execution:數(shù)據(jù)在內(nèi)存中按列存儲,以數(shù)組表示。選擇數(shù)組的大小,讓其可以在L1 data cache中裝得下,然后執(zhí)行樹的每個算子執(zhí)行tight for-loop按數(shù)組處理數(shù)據(jù)。這樣即避免了full materialization過多的資源索取,又能避免tuple-at-a-time的處理單個tuple的overhead,同時對cache更加友好,減少spurious invalidation,提升speculative cache missing的產(chǎn)生。同時簡單tight for-loop,編譯器能更好點編譯成高效執(zhí)行指令,同時也能更好地填充CPU的指令流水,充分利用CPU的multiple issue的功能加速簡單指令的處理。

在存儲層,數(shù)據(jù)采用列式存儲,可獲得很好的編碼效率,降低讀放大和空間放大,訪問持久化存儲的帶寬被高效利用,CPU和Memory的帶寬增速相對于持久化存儲帶寬增速表現(xiàn)出了顯著的差異,使用CPU的計算交換磁盤帶寬,提升了數(shù)據(jù)的處理能力。

列存,向量化執(zhí)行引擎,MPP是現(xiàn)代分析性數(shù)據(jù)庫的關鍵技術。

5.多核

從編程實現(xiàn)的角度看,多核scale,CC協(xié)議,共識算法是計算機中比較有挑戰(zhàn),并且是純自然的問題。即便技術上不深入接觸數(shù)據(jù)庫,也對這三個問題相當?shù)匕V迷,被數(shù)據(jù)庫技術的吸引加入這個領域,或多或少和這三個問題相關。

為什么多核如此重要呢?

假設摩爾定律,沒有功率墻的限制,世界會怎樣呢?顯然我們不需要修改老代碼,只要增加單核晶體管數(shù)量,老代碼自然而然會提升。我們撞到了功率墻后,發(fā)現(xiàn)需要增加核數(shù)以提升計算速度?,F(xiàn)在問題來了,我們的代碼已經(jīng)寫成了多線程執(zhí)行,那么隨著核數(shù)增長,修改worker線程池的大小,老代碼的計算能力會隨著核數(shù)增加而持續(xù)增加嗎?顯然不是這樣,因為多核上的scale受到阿姆達爾定律的制約,當代碼中串行執(zhí)行的部分占比1%時,256核機器只能最多加速到72倍,如果是10%,只能最多加速到10倍。顯然修改線程池的大小,并不是一個好方法,減少代碼中contention才是關鍵。

這種情況下,speedup想要隨著核數(shù)而scale,發(fā)現(xiàn)很多算法,數(shù)據(jù)結(jié)構(gòu),CC協(xié)議和分析處理的算子實現(xiàn),需要case-by-case重構(gòu)以減少contention,重構(gòu)方式是采用lockfree算法。但是這還不是事實的全部,當面對多核scale時,其實我們面對的是一個新的分布式系統(tǒng),這個新的分布式系統(tǒng)是以interconnect為網(wǎng)絡,以核為計算資源,并且還需要考慮屏蔽內(nèi)存體系的延遲。如果說原來的分布式系統(tǒng)中,我們傾向于每個機器各干各的,數(shù)據(jù)做到均衡,計算資源就可以充分利用。對于多核編程同樣有這個問題,怎么將原來的任務均勻地拆成多個子任務,然后多個子任務可以齊頭并進,幾乎同時沖線結(jié)束。顯然數(shù)據(jù)拆分不均衡,跨核通信等因素都會造成快核等慢核的問題。同時,多核處理時,傾向于協(xié)作完成一個共同的任務,而非各干各的,這種情況下,將任務均勻拆分成子任務的的調(diào)度代碼,共享的數(shù)據(jù)結(jié)構(gòu)的訪問代碼,多核彼此之間等待本身就是同步點,即contention,總之,contention怎么降低呢?

現(xiàn)實中,lockfree算法,怎么描述和驗證正確性呢?我們對比其他兩個問題的思路,或許有解法。比如共識算法中,采用invariant描述算法;而CC協(xié)議中,采用反例(anormaly)攻擊算法?;蛟S這兩種方法相互結(jié)合,能夠幫助我們研究lockfree算法。

多核scale的挑戰(zhàn)性很大,但這可以讓具有優(yōu)勢的傳統(tǒng)數(shù)據(jù)庫和數(shù)據(jù)庫的新進入者,處于賽道的同一起跑線,比拼誰的代碼case-by-case優(yōu)化做得好。

也有不少團隊,在思考異構(gòu)計算加速數(shù)據(jù)處理,這同樣充滿機會。但是,依靠程序員的心智構(gòu)造精巧而高質(zhì)量的代碼,費時費力?;蛟S的確應該通過編譯器的后端技術一勞永逸解決這類問題,現(xiàn)在做不到,不代表未來也做不到。到時候,有人看著前人寫得如此復雜的代碼,就好比我們現(xiàn)在看到泥板書和帶孔的卡片。

6.計算

計算主要講執(zhí)行引擎,執(zhí)行引擎是一個很大的scope,目前roadmap已經(jīng)建立,但是缺乏baseline,待兩者都ready之后,會補充。

7.編譯

編譯對數(shù)據(jù)庫的滲透是全方位的:比如計算引擎在向量化之外可采用編譯技術優(yōu)化性能。數(shù)據(jù)庫中很多case-by-case的性能優(yōu)化,需要深入研究體系結(jié)構(gòu),異構(gòu)計算加速處理也需要使用編譯技術。流批DSL腳本使用現(xiàn)有的SQL執(zhí)行引擎做計算,UDF擴充等。目前動機已經(jīng)明了,但roadmap和baseline尚未建立,兩者ready之后,也會補充進來。

THEEND

最新評論(評論僅代表用戶觀點)

更多
暫無評論