在分佈式系統中,有不少臺node組成一個cluster,對於client 的一個寫操做請求而言,在什麼樣的狀況下,cluster告訴client這次寫操做請求是成功的呢?html
假設有一個三節點的cluster,一個primary node,兩個replica node以下圖所示:
node
方案1,client有着良好的響應性,由於只須要primary持久化了,就給client響應;併發性更高,由於對於cluster而言,只須要primary持久化了,cluster 就算「暫時」成功處理了一個請求。緩存
可是對於client而言,讀操做就不方便了。由於,client要想讀到最新的數據,讀primary node是沒問題的,可是讀 replic node就有可能讀不到最新的數據,由於:primary 還將來得及將最新的數據 複製到 replica 時,client向replica 發起了讀數據請求,以下圖所示:
數據結構
這樣,client就讀不到它剛纔明明已經寫入成功的數據了。併發
方案2,client 讀取任何一臺node都能讀取到最新寫入的數據,可是client的響應性不佳。app
上面的討論是從client角度、cluster角度來討論的,還能夠從數據的角度來討論,就是數據是否被丟失。異步
一個cluster,通常是要接收大量的client的寫請求的,若是來一個寫請求,cluster就執行一次磁盤寫操做(將數據持久化)那麼性能應該是不佳的。所以,能夠先將若干個寫請求的數據都放到一個buffer中,而後等一段時間再批量將這些數據刷新到磁盤(sync)。elasticsearch
當引入了buffer,將寫操做數據批量寫磁盤 這種機制時,何時給client返回寫操做成功的響應呢?是等若干個寫操做數據批量同步到磁盤後,再給client返回寫請求成功的響應、仍是數據只是存儲到所謂的buffer裏面,就給client返回寫操做成功的響應?而buffer的引入,又對 primary node 將數據 複製到 replica node 產生何種影響?分佈式
另外,就算真的不引入所謂的buffer,若是client的寫操做很複雜、代價很大,難不成真的是要等數據持久化到磁盤才能給client響應成功嗎?這種方式是否是有點與「併發控制 鎖操做中的」悲觀鎖……高併發
這個真的就很差說了,可能不一樣的產品有不一樣的實現細節吧。好比ElasticSearch、Mongodb
而當引入了buffer以後,因爲將client的寫操做數據都批量緩存起來了,那萬一機器掛了,那這些緩存的數據就全丟失了,而若是client發一個請求,就同步一次磁盤,那處理性能又受到了影響,這彷佛是一個兩難的問題。
所以,爲了解決這個問題,引入了一個叫「replication log」的概念。relication log有多種不一樣的實現方式,好比 write ahead log(WAL),而在ElasticSearch裏面也有一個相似的東西,叫作transaction log(不知道我理解的對不對)
replication log的思想就是:針對client的寫操做,生成一條日誌,該日誌詳細記錄了寫操做對數據進行何種操做。通常日誌只支持append操做的,通常地,相比於寫數據操做、寫日誌要輕量級得多。另外日誌還有個好處是:若是node在寫操做過程當中失敗了,好比數據寫到一半失敗了,那及有可能形成數據的不一致性,那它還能夠再讀取日誌,從日誌中恢復出來。
下面來舉個具體的例子,我的理解。
client 向 elasticsearch cluster 發起 index 操做。文檔要分詞,到Lucene底層要構造segment,生成 倒排索引(posting list)這種數據結構。這是一種「費時費力、代價很大的操做」,所以,不可能 針對 一篇文檔一個index請求,就flush 一次segment。所以能夠每隔一段時間、批量flush segment。而若是批量flush 的話,若是機器宕機了,那就會丟失不少數據。所以,es 引入了translog:
all the index/delete/update operations are written to the translog and the translog is fsynced after every index/delete/update operations to make sure the changes are persistent. the client receives acknowledgement for writes after the translog is fsynced on both primary and replic shards
寫translog 應該要比 lucene segment flush 操做要輕量級得多。另外,primary shards 只須要將translog持久化、並同步給replica 後,就能夠給client返回 寫操做成功的響應了,這樣可支持寫操做的高併發。
本文從三個角度:client、cluster、數據是否丟失 闡述了分佈式中的讀寫一致性及數據可靠性。
我的理解,可能有錯。
原文:http://www.javashuo.com/article/p-mwqdybdg-my.html