分布式中的 transaction log
在分布式系統中,有很多台node組成一個cluster,對於client 的一個寫操作請求而言,在什么樣的情況下,cluster告訴client此次寫操作請求是成功的呢?
首先來定義一下什么是寫操作成功?
假設有一個三節點的cluster,一個primary node,兩個replica node如下圖所示:
- 方案1. client 向primary發寫請求,primary 將待寫的數據持久化后,返回響應給client此次寫操作成功,然后primary再將數據復制給其他兩台replica node。
- 方案2. client 向primary發寫請求,primary 將待寫的數據 復制 到其他兩台 replica node,等待 replic node將數據持久化后給primary響應,然后primary再返回響應給client此次寫操作成功。
方案1,client有着良好的響應性,因為只需要primary持久化了,就給client響應;並發性更高,因為對於cluster而言,只需要primary持久化了,cluster 就算“暫時”成功處理了一個請求。
但是對於client而言,讀操作就不方便了。因為,client要想讀到最新的數據,讀primary node是沒問題的,但是讀 replic node就有可能讀不到最新的數據,因為:primary 還未來得及將最新的數據 復制到 replica 時,client向replica 發起了讀數據請求,如下圖所示:
這樣,client就讀不到它剛才明明已經寫入成功的數據了。
方案2,client 讀取任何一台node都能讀取到最新寫入的數據,但是client的響應性不佳。
上面的討論是從client角度、cluster角度來討論的,還可以從數據的角度來討論,就是數據是否被丟失。
一個cluster,一般是要接收大量的client的寫請求的,如果來一個寫請求,cluster就執行一次磁盤寫操作(將數據持久化)那么性能應該是不佳的。因此,可以先將若干個寫請求的數據都放到一個buffer中,然后等一段時間再批量將這些數據刷新到磁盤(sync)。
當引入了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、數據是否丟失 闡述了分布式中的讀寫一致性及數據可靠性。
- 節點之間的數據復制方式(同步復制、異步復制)是宏觀上(針對的是各個node)的一種影響數據可靠性的因素
- replication log(transaction log)是一種微觀上(針對的是具體的寫操作)影響數據可靠性的因素
個人理解,可能有錯。
原文:https://www.cnblogs.com/hapjin/p/9655879.html