聊聊 Kafka:Kafka 如何保證一致性

一续室、前言

在如今的分布式環(huán)境時代闯袒,任何一款中間件產(chǎn)品幔嗦,大多都有一套機(jī)制去保證一致性的酿愧,Kafka 作為一個商業(yè)級消息中間件,消息一致性的重要性可想而知邀泉,那 Kafka 如何保證一致性的呢嬉挡?本文從高水位更新機(jī)制、副本同步機(jī)制以及 Leader Epoch 幾個方面去介紹 Kafka 是如何保證一致性的汇恤。

二庞钢、HW 和 LEO

要想 Kafka 保證一致性,我們必須先了解 HW(High Watermark)高水位和 LEO(Log End Offset)日志末端位移因谎,看下面這張圖你就清晰了:

image.png

高水位的作用:

  • 定義消息可見性基括,即用來標(biāo)識分區(qū)下的哪些消息是可以被消費(fèi)者消費(fèi)的。
  • 幫助 Kafka 完成副本同步

這里我們不討論 Kafka 事務(wù)蓝角,因?yàn)槭聞?wù)機(jī)制會影響消費(fèi)者所能看到的消息的范圍阱穗,它不只是簡單依賴高水位來判斷。它依靠一個名為 LSO(Log Stable Offset)的位移值來判斷事務(wù)型消費(fèi)者的可見性使鹅。

日志末端位移的作用:

  • 副本寫入下一條消息的位移值
  • 數(shù)字 15 所在的方框是虛線揪阶,這就說明,這個副本當(dāng)前只有 15 條消息患朱,位移值是從 0 到 14鲁僚,下一條新消息的位移是 15。
  • 介于高水位和 LEO 之間的消息就屬于未提交消息。這也反應(yīng)出一個事實(shí)冰沙,那就是:同一個副本對象侨艾,其高水位值不會大于 LEO 值。

高水位和 LEO 是副本對象的兩個重要屬性拓挥。Kafka 所有副本都有對應(yīng)的高水位和 LEO 值唠梨,而不僅僅是 Leader 副本。只不過 Leader 副本比較特殊侥啤,Kafka 使用 Leader 副本的高水位來定義所在分區(qū)的高水位当叭。換句話說,分區(qū)的高水位就是其 Leader 副本的高水位盖灸。

三蚁鳖、HW 和 LEO 的更新機(jī)制

現(xiàn)在,我們知道了每個副本對象都保存了一組高水位值和 LEO 值赁炎,但實(shí)際上醉箕,在 Leader 副本所在的 Broker 上,還保存了其他 Follower 副本的 LEO 值徙垫,請看下圖:

image.png

從圖中可以看出讥裤,Broker 0 上保存了某分區(qū)的 Leader 副本和所有 Follower 副本的 LEO 值,而 Broker 1 上僅僅保存了該分區(qū)的某個 Follower 副本姻报。Kafka 把 Broker 0 上保存的這些 Follower 副本又稱為遠(yuǎn)程副本(Remote Replica)坞琴。Kafka 副本機(jī)制在運(yùn)行過程中,會更新 Broker 1 上 Follower 副本的高水位和 LEO 值逗抑,同時也會更新 Broker 0 上 Leader 副本的高水位和 LEO 以及所有遠(yuǎn)程副本的 LEO,但它不會更新遠(yuǎn)程副本的高水位值寒亥,也就是我在圖中標(biāo)記為灰色的部分邮府。

這里你可能就困惑了?

  • 為啥 Leader 副本所在的 Broker 上溉奕,還保存了其他 Follower 副本的 LEO 值褂傀?
  • 為啥 Leader 副本所在的 Broker 上不會更新 Follower 副本 HW?

別著急加勤,老周帶你看下源碼:

kafka.cluster.Partition#makeLeader 中:

image.png
image.png

Leader 副本所在的 Broker 上只有重置更新遠(yuǎn)程副本的 LEO仙辟,并沒有遠(yuǎn)程副本的 HW。

這里你又可能會問了鳄梅?

  • 為什么要在 Broker 0 上保存這些遠(yuǎn)程副本呢叠国?
  • Broker 0 不會更新遠(yuǎn)程副本 HW,那遠(yuǎn)程副本的 HW 的更新機(jī)制又是怎樣的呢戴尸?

Broker 0 上保存這些遠(yuǎn)程副本的主要作用是粟焊,幫助 Leader 副本確定其高水位,也就是分區(qū)高水位。

第二個問題我們直接來看下 HW 和 LEO 被更新的時機(jī):

image.png

3.1 Leader 副本

處理生產(chǎn)者請求的邏輯如下:

  • 寫入消息到本地磁盤
  • 更新分區(qū)高水位值
    • 獲取 Leader 副本所在 Broker 端保存的所有遠(yuǎn)程副本 LEO 值(LEO-1项棠,LEO-2悲雳,……,LEO-n)
    • 獲取 Leader 副本高水位值:currentHW
    • 更新 currentHW = max{currentHW, min(LEO-1, LEO-2, ……香追,LEO-n)}

處理 Follower 副本拉取消息的邏輯如下:

  • 讀取磁盤(或頁緩存)中的消息數(shù)據(jù)
  • 使用 Follower 副本發(fā)送請求中的位移值更新遠(yuǎn)程副本 LEO 值
  • 更新分區(qū)高水位值(具體步驟與處理生產(chǎn)者請求的步驟相同)

3.2 Follower 副本

從 Leader 拉取消息的處理邏輯如下:

  • 寫入消息到本地磁盤
  • 更新 LEO 值
  • 更新高水位值
    • 獲取 Leader 發(fā)送的高水位值:currentHW
    • 獲取步驟 2 中更新過的 LEO 值:currentLEO
    • 更新高水位為 min(currentHW, currentLEO)

四合瓢、副本同步機(jī)制

搞清楚了上面 HW 和 LEO 的更新機(jī)制后,我們舉一個單分區(qū)且有兩個副本的主題來演示下 Kafka 副本同步的全流程透典。

當(dāng)生產(chǎn)者發(fā)送一條消息時晴楔,Leader 和 Follower 副本對應(yīng)的 HW 和 LEO 是怎么被更新的呢?

首先是初始狀態(tài)掷匠。下面這張圖中的 remote LEO 就是剛才的遠(yuǎn)程副本的 LEO 值滥崩。在初始狀態(tài)時,所有值都是 0讹语。

image.png

當(dāng)生產(chǎn)者給主題分區(qū)發(fā)送一條消息后钙皮,狀態(tài)變更為:

image.png

此時,Leader 副本成功將消息寫入了本地磁盤顽决,故 LEO 值被更新為 1短条。

Follower 再次嘗試從 Leader 拉取消息。和之前不同的是才菠,這次有消息可以拉取了茸时,因此狀態(tài)進(jìn)一步變更為:

image.png

這時,F(xiàn)ollower 副本也成功地更新 LEO 為 1赋访。此時可都,Leader 和 Follower 副本的 LEO 都是 1,但各自的高水位依然是 0蚓耽,還沒有被更新渠牲。它們需要在下一輪的拉取中被更新,如下圖所示:

image.png

在新一輪的拉取請求中步悠,由于位移值是 0 的消息已經(jīng)拉取成功签杈,因此 Follower 副本這次請求拉取的是位移值 =1 的消息。Leader 副本接收到此請求后鼎兽,更新遠(yuǎn)程副本 LEO 為 1答姥,然后更新 Leader 高水位為 1。做完這些之后谚咬,它會將當(dāng)前已更新過的高水位值 1 發(fā)送給 Follower 副本鹦付。Follower 副本接收到以后,也將自己的高水位值更新成 1择卦。至此睁壁,一次完整的消息同步周期就結(jié)束了背苦。事實(shí)上,Kafka 就是利用這樣的機(jī)制潘明,實(shí)現(xiàn)了 Leader 和 Follower 副本之間的同步行剂。

五、Leader Epoch 機(jī)制

上面的副本同步機(jī)制似乎很完美钳降,我們不妨來思考下這種場景:

從剛才的分析中厚宰,我們知道,F(xiàn)ollower 副本的高水位更新需要一輪額外的拉取請求才能實(shí)現(xiàn)遂填。如果把上面那個例子擴(kuò)展到多個 Follower 副本铲觉,情況可能更糟,也許需要多輪拉取請求吓坚。也就是說撵幽,Leader 副本高水位更新和 Follower 副本高水位更新在時間上是存在錯配的。這種錯配是很多“數(shù)據(jù)丟失”或“數(shù)據(jù)不一致”問題的根源礁击⊙卧樱基于此,社區(qū)在 0.11 版本正式引入了 Leader Epoch 概念哆窿,來規(guī)避因高水位更新錯配導(dǎo)致的各種不一致問題链烈。

所謂 Leader Epoch,我們大致可以認(rèn)為是 Leader 版本挚躯。它由兩部分?jǐn)?shù)據(jù)組成强衡。

  • Epoch。一個單調(diào)增加的版本號码荔。每當(dāng)副本領(lǐng)導(dǎo)權(quán)發(fā)生變更時漩勤,都會增加該版本號。小版本號的 Leader 被認(rèn)為是過期 Leader缩搅,不能再行使 Leader 權(quán)力锯七。
  • 起始位移(Start Offset)。Leader 副本在該 Epoch 值上寫入的首條消息的位移誉己。

我舉個例子來說明一下 Leader Epoch。假設(shè)現(xiàn)在有兩個 Leader Epoch<0, 0> 和 <1, 120>域蜗,那么巨双,第一個 Leader Epoch 表示版本號是 0,這個版本的 Leader 從位移 0 開始保存消息霉祸,一共保存了 120 條消息筑累。之后,Leader 發(fā)生了變更丝蹭,版本號增加到 1慢宗,新版本的起始位移是 120。

Kafka Broker 會在內(nèi)存中為每個分區(qū)都緩存 Leader Epoch 數(shù)據(jù),同時它還會定期地將這些信息持久化到一個 checkpoint 文件中镜沽。當(dāng) Leader 副本寫入消息到磁盤時敏晤,Broker 會嘗試更新這部分緩存。如果該 Leader 是首次寫入消息缅茉,那么 Broker 會向緩存中增加一個 Leader Epoch 條目嘴脾,否則就不做更新。這樣蔬墩,每次有 Leader 變更時译打,新的 Leader 副本會查詢這部分緩存,取出對應(yīng)的 Leader Epoch 的起始位移拇颅,以避免數(shù)據(jù)丟失和不一致的情況奏司。

源碼在 org.apache.kafka.raft.LeaderState 中:

image.png

Kafka Broker 會在內(nèi)存中為每個分區(qū)都緩存 Leader Epoch 數(shù)據(jù):

image.png

同時它還會定期地將這些信息持久化到一個 checkpoint 文件中:

org.apache.kafka.common.message.LeaderAndIsrRequestData.LeaderAndIsrPartitionState#write

image.png

接下來,我們來看一個實(shí)際的例子樟插,它展示的是 Leader Epoch 是如何防止數(shù)據(jù)丟失的韵洋。請先看下圖:

image.png

開始時,副本 A 和副本 B 都處于正常狀態(tài)岸夯,A 是 Leader 副本麻献。某個使用了默認(rèn) acks 設(shè)置的生產(chǎn)者程序向 A 發(fā)送了兩條消息,A 全部寫入成功猜扮,此時 Kafka 會通知生產(chǎn)者說兩條消息全部發(fā)送成功勉吻。

現(xiàn)在我們假設(shè) Leader 和 Follower 都寫入了這兩條消息,而且 Leader 副本的高水位也已經(jīng)更新了旅赢,但 Follower 副本高水位還未更新——這是可能出現(xiàn)的齿桃。還記得吧,F(xiàn)ollower 端高水位的更新與 Leader 端有時間錯配煮盼。倘若此時副本 B 所在的 Broker 宕機(jī)短纵,當(dāng)它重啟回來后,副本 B 會執(zhí)行日志截斷操作僵控,將 LEO 值調(diào)整為之前的高水位值香到,也就是 1。這就是說报破,位移值為 1 的那條消息被副本 B 從磁盤中刪除悠就,此時副本 B 的底層磁盤文件中只保存有 1 條消息,即位移值為 0 的那條消息充易。

當(dāng)執(zhí)行完截斷操作后梗脾,副本 B 開始從 A 拉取消息,執(zhí)行正常的消息同步盹靴。如果就在這個節(jié)骨眼上炸茧,副本 A 所在的 Broker 宕機(jī)了瑞妇,那么 Kafka 就別無選擇,只能讓副本 B 成為新的 Leader梭冠,此時辕狰,當(dāng) A 回來后,需要執(zhí)行相同的日志截斷操作妈嘹,即將高水位調(diào)整為與 B 相同的值柳琢,也就是 1。這樣操作之后润脸,位移值為 1 的那條消息就從這兩個副本中被永遠(yuǎn)地抹掉了柬脸。這就是這張圖要展示的數(shù)據(jù)丟失場景。

嚴(yán)格來說毙驯,這個場景發(fā)生的前提是 Broker 端參數(shù) min.insync.replicas 設(shè)置為 1倒堕。此時一旦消息被寫入到 Leader 副本的磁盤,就會被認(rèn)為是“已提交狀態(tài)”爆价,但現(xiàn)有的時間錯配問題導(dǎo)致 Follower 端的高水位更新是有滯后的垦巴。如果在這個短暫的滯后時間窗口內(nèi),接連發(fā)生 Broker 宕機(jī)铭段,那么這類數(shù)據(jù)的丟失就是不可避免的骤宣。

現(xiàn)在,我們來看下如何利用 Leader Epoch 機(jī)制來規(guī)避這種數(shù)據(jù)丟失序愚。請看下圖:

image.png

場景和之前大致是類似的憔披,只不過引用 Leader Epoch 機(jī)制后,F(xiàn)ollower 副本 B 重啟回來后爸吮,需要向 A 發(fā)送一個特殊的請求去獲取 Leader 的 LEO 值芬膝。在這個例子中,該值為 2形娇。當(dāng)獲知到 Leader LEO=2 后锰霜,B 發(fā)現(xiàn)該 LEO 值不比它自己的 LEO 值小,而且緩存中也沒有保存任何起始位移值 > 2 的 Epoch 條目桐早,因此 B 無需執(zhí)行任何日志截斷操作癣缅。這是對高水位機(jī)制的一個明顯改進(jìn),即副本是否執(zhí)行日志截斷不再依賴于高水位進(jìn)行判斷哄酝。

現(xiàn)在友存,副本 A 宕機(jī)了,B 成為 Leader炫七。同樣地,當(dāng) A 重啟回來后钾唬,執(zhí)行與 B 相同的邏輯判斷万哪,發(fā)現(xiàn)也不用執(zhí)行日志截斷侠驯,至此位移值為 1 的那條消息在兩個副本中均得到保留。后面當(dāng)生產(chǎn)者程序向 B 寫入新消息時奕巍,副本 B 所在的 Broker 緩存中吟策,會生成新的 Leader Epoch 條目:[Epoch=1, Offset=2]。之后的止,副本 B 會使用這個條目幫助判斷后續(xù)是否執(zhí)行日志截斷操作檩坚。這樣,通過 Leader Epoch 機(jī)制诅福,Kafka 完美地規(guī)避了這種數(shù)據(jù)丟失場景匾委。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市氓润,隨后出現(xiàn)的幾起案子赂乐,更是在濱河造成了極大的恐慌,老刑警劉巖咖气,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挨措,死亡現(xiàn)場離奇詭異,居然都是意外死亡崩溪,警方通過查閱死者的電腦和手機(jī)浅役,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伶唯,“玉大人觉既,你說我怎么就攤上這事〉衷酰” “怎么了奋救?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長反惕。 經(jīng)常有香客問我尝艘,道長,這世上最難降的妖魔是什么姿染? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任背亥,我火速辦了婚禮,結(jié)果婚禮上悬赏,老公的妹妹穿的比我還像新娘狡汉。我一直安慰自己,他們只是感情好闽颇,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布盾戴。 她就那樣靜靜地躺著,像睡著了一般兵多。 火紅的嫁衣襯著肌膚如雪尖啡。 梳的紋絲不亂的頭發(fā)上橄仆,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機(jī)與錄音衅斩,去河邊找鬼盆顾。 笑死,一個胖子當(dāng)著我的面吹牛畏梆,可吹牛的內(nèi)容都是我干的您宪。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼奠涌,長吁一口氣:“原來是場噩夢啊……” “哼宪巨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起铣猩,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤揖铜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后达皿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體天吓,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年峦椰,在試婚紗的時候發(fā)現(xiàn)自己被綠了龄寞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡汤功,死狀恐怖物邑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情滔金,我是刑警寧澤色解,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站餐茵,受9級特大地震影響科阎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜忿族,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一锣笨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧道批,春花似錦错英、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春判哥,著一層夾襖步出監(jiān)牢的瞬間氮唯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工姨伟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人豆励。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓夺荒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親良蒸。 傳聞我的和親對象是個殘疾皇子技扼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容

  • Kafka的也存在Leader和Follow節(jié)點(diǎn),這樣就會有一致性問題嫩痰。 概念 1. ?位標(biāo)記 ?位或?咏宋恰(wat...
    david161閱讀 1,681評論 0 1
  • 一、前言 在如今的分布式環(huán)境時代串纺,任何一款中間件產(chǎn)品丽旅,大多都有一套機(jī)制去保證高可用的,Kafka 作為一個商業(yè)級消...
    老周聊架構(gòu)閱讀 721評論 0 1
  • 簡單總結(jié):消費(fèi)端重復(fù)消費(fèi):建立去重表消費(fèi)端丟失數(shù)據(jù):關(guān)閉自動提交offset纺棺,處理完之后受到移位榄笙,enable.a...
    請不要問我是誰閱讀 13,702評論 0 4
  • Kafka高級特性解析(三) 物理存儲 日志存儲概述 Kafka 消息是以主題為單位進(jìn)行歸類,各個主題之間是彼此獨(dú)...
    奮斗的蛐蛐閱讀 649評論 0 0
  • [TOC]Kafka 中采用了多副本的機(jī)制祷蝌,這是大多數(shù)分布式系統(tǒng)中慣用的手法茅撞,以此來實(shí)現(xiàn)水平擴(kuò) 展、提供容災(zāi)能力巨朦、...
    tracy_668閱讀 878評論 0 2