學(xué)過大數(shù)據(jù)的同學(xué)應(yīng)該都知道 Kafka凉翻,它是分布式消息訂閱系統(tǒng),有非常好的橫向擴(kuò)展性制轰,可實(shí)時(shí)存儲海量數(shù)據(jù),是流數(shù)據(jù)處理中間件的事實(shí)標(biāo)準(zhǔn)垃杖。本文將介紹 Kafka 是如何保證數(shù)據(jù)可靠性和一致性的男杈。
數(shù)據(jù)可靠性
Kafka 作為一個(gè)商業(yè)級消息中間件调俘,消息可靠性的重要性可想而知。本文從 Producter 往 Broker 發(fā)送消息彩库、Topic 分區(qū)副本以及 Leader 選舉幾個(gè)角度介紹數(shù)據(jù)的可靠性肤无。
Topic 分區(qū)副本
在 Kafka 0.8.0 之前骇钦,Kafka 是沒有副本的概念的,那時(shí)候人們只會用 Kafka 存儲一些不重要的數(shù)據(jù)司忱,因?yàn)闆]有副本皇忿,數(shù)據(jù)很可能會丟失坦仍。但是隨著業(yè)務(wù)的發(fā)展,支持副本的功能越來越強(qiáng)烈繁扎,所以為了保證數(shù)據(jù)的可靠性,Kafka 從 0.8.0 版本開始引入了分區(qū)副本(詳情請參見 KAFKA-50)梳玫。也就是說每個(gè)分區(qū)可以人為的配置幾個(gè)副本(比如創(chuàng)建主題的時(shí)候指定 replication-factor爹梁,也可以在 Broker 級別進(jìn)行配置 default.replication.factor)提澎,一般會設(shè)置為3姚垃。
Kafka 可以保證單個(gè)分區(qū)里的事件是有序的盼忌,分區(qū)可以在線(可用)积糯,也可以離線(不可用)谦纱。在眾多的分區(qū)副本里面有一個(gè)副本是 Leader看成,其余的副本是 follower,所有的讀寫操作都是經(jīng)過 Leader 進(jìn)行的跨嘉,同時(shí) follower 會定期地去 leader 上的復(fù)制數(shù)據(jù)。當(dāng) Leader 掛了的時(shí)候祠乃,其中一個(gè) follower 會重新成為新的 Leader梦重。通過分區(qū)副本亮瓷,引入了數(shù)據(jù)冗余琴拧,同時(shí)也提供了 Kafka 的數(shù)據(jù)可靠性寺庄。
Kafka 的分區(qū)多副本架構(gòu)是 Kafka 可靠性保證的核心,把消息寫入多個(gè)副本可以使 Kafka 在發(fā)生崩潰時(shí)仍能保證消息的持久性斗塘。
Producer 往 Broker 發(fā)送消息
如果我們要往 Kafka 對應(yīng)的主題發(fā)送消息,我們需要通過 Producer 完成馍盟。前面我們講過 Kafka 主題對應(yīng)了多個(gè)分區(qū)于置,每個(gè)分區(qū)下面又對應(yīng)了多個(gè)副本贞岭;為了讓用戶設(shè)置數(shù)據(jù)可靠性, Kafka 在 Producer 里面提供了消息確認(rèn)機(jī)制瞄桨。也就是說我們可以通過配置來決定消息發(fā)送到對應(yīng)分區(qū)的幾個(gè)副本才算消息發(fā)送成功话速⌒窘模可以在定義 Producer 時(shí)通過 acks 參數(shù)指定(在 0.8.2.X 版本之前是通過 request.required.acks 參數(shù)設(shè)置的乳讥,詳見 KAFKA-3043)。這個(gè)參數(shù)支持以下三種值:
acks = 0:意味著如果生產(chǎn)者能夠通過網(wǎng)絡(luò)把消息發(fā)送出去廓俭,那么就認(rèn)為消息已成功寫入 Kafka 。在這種情況下還是有可能發(fā)生錯(cuò)誤研乒,比如發(fā)送的對象無能被序列化或者網(wǎng)卡發(fā)生故障,但如果是分區(qū)離線或整個(gè)集群長時(shí)間不可用雹熬,那就不會收到任何錯(cuò)誤。在 acks=0 模式下的運(yùn)行速度是非抽匣#快的(這就是為什么很多基準(zhǔn)測試都是基于這個(gè)模式),你可以得到驚人的吞吐量和帶寬利用率仰楚,不過如果選擇了這種模式, 一定會丟失一些消息僧界。
acks = 1:意味若 Leader 在收到消息并把它寫入到分區(qū)數(shù)據(jù)文件(不一定同步到磁盤上)時(shí)會返回確認(rèn)或錯(cuò)誤響應(yīng)。在這個(gè)模式下捂襟,如果發(fā)生正常的 Leader 選舉咬腕,生產(chǎn)者會在選舉時(shí)收到一個(gè) LeaderNotAvailableException 異常葬荷,如果生產(chǎn)者能恰當(dāng)?shù)靥幚磉@個(gè)錯(cuò)誤涨共,它會重試發(fā)送悄息宠漩,最終消息會安全到達(dá)新的 Leader 那里举反。不過在這個(gè)模式下仍然有可能丟失數(shù)據(jù)扒吁,比如消息已經(jīng)成功寫入 Leader,但在消息被復(fù)制到 follower 副本之前 Leader發(fā)生崩潰雕崩。
acks = all(這個(gè)和 request.required.acks = -1 含義一樣):意味著 Leader 在返回確認(rèn)或錯(cuò)誤響應(yīng)之前魁索,會等待所有同步副本都收到悄息盼铁。如果和 min.insync.replicas 參數(shù)結(jié)合起來,就可以決定在返回確認(rèn)前至少有多少個(gè)副本能夠收到悄息饶火,生產(chǎn)者會一直重試直到消息被成功提交冬念。不過這也是最慢的做法趁窃,因?yàn)樯a(chǎn)者在繼續(xù)發(fā)送其他消息之前需要等待所有副本都收到當(dāng)前的消息。
根據(jù)實(shí)際的應(yīng)用場景醒陆,我們設(shè)置不同的 acks,以此保證數(shù)據(jù)的可靠性刨摩。
另外寺晌,Producer 發(fā)送消息還可以選擇同步(默認(rèn)澡刹,通過 producer.type=sync 配置) 或者異步(producer.type=async)模式。如果設(shè)置成異步罢浇,雖然會極大的提高消息發(fā)送的性能陆赋,但是這樣會增加丟失數(shù)據(jù)的風(fēng)險(xiǎn)嚷闭。如果需要確保消息的可靠性,必須將 producer.type 設(shè)置為 sync胞锰。
Leader 選舉
在介紹 Leader 選舉之前灾锯,讓我們先來了解一下 ISR(in-sync replicas)列表嗅榕。每個(gè)分區(qū)的 leader 會維護(hù)一個(gè) ISR 列表,ISR 列表里面就是 follower 副本的 Borker 編號凌那,只有跟得上 Leader 的 follower 副本才能加入到 ISR 里面兼雄,這個(gè)是通過 replica.lag.time.max.ms 參數(shù)配置的案怯。只有 ISR 里的成員才有被選為 leader 的可能。
所以當(dāng) Leader 掛掉了嘲碱,而且 unclean.leader.election.enable=false 的情況下,Kafka 會從 ISR 列表中選擇第一個(gè) follower 作為新的 Leader麦锯,因?yàn)檫@個(gè)分區(qū)擁有最新的已經(jīng) committed 的消息恕稠。通過這個(gè)可以保證已經(jīng) committed 的消息的數(shù)據(jù)可靠性扶欣。
綜上所述鹅巍,為了保證數(shù)據(jù)的可靠性,我們最少需要配置一下幾個(gè)參數(shù):
producer 級別:acks=all(或者 request.required.acks=-1)骆捧,同時(shí)發(fā)生模式為同步 producer.type=sync
topic 級別:設(shè)置 replication.factor>=3,并且 min.insync.replicas>=2敛苇;
broker 級別:關(guān)閉不完全的 Leader 選舉,即 unclean.leader.election.enable=false枫攀;
數(shù)據(jù)一致性
這里介紹的數(shù)據(jù)一致性主要是說不論是老的 Leader 還是新選舉的 Leader,Consumer 都能讀到一樣的數(shù)據(jù)来涨。那么 Kafka 是如何實(shí)現(xiàn)的呢?
假設(shè)分區(qū)的副本為3蹦掐,其中副本0是 Leader,副本1和副本2是 follower笤闯,并且在 ISR 列表里面。雖然副本0已經(jīng)寫入了 Message4颗味,但是 Consumer 只能讀取到 Message2超陆。因?yàn)樗械?ISR 都同步了 Message2浦马,只有 High Water Mark 以上的消息才支持 Consumer 讀取时呀,而 High Water Mark 取決于 ISR 列表里面偏移量最小的分區(qū)晶默,對應(yīng)于上圖的副本2,這個(gè)很類似于木桶原理磺陡。
這樣做的原因是還沒有被足夠多副本復(fù)制的消息被認(rèn)為是“不安全”的趴梢,如果 Leader 發(fā)生崩潰币他,另一個(gè)副本成為新 Leader,那么這些消息很可能丟失了蝴悉。如果我們允許消費(fèi)者讀取這些消息彰阴,可能就會破壞一致性拍冠。試想簇抵,一個(gè)消費(fèi)者從當(dāng)前 Leader(副本0) 讀取并處理了 Message4,這個(gè)時(shí)候 Leader 掛掉了射众,選舉了副本1為新的 Leader,這時(shí)候另一個(gè)消費(fèi)者再去從新的 Leader 讀取消息责球,發(fā)現(xiàn)這個(gè)消息其實(shí)并不存在,這就導(dǎo)致了數(shù)據(jù)不一致性問題雏逾。
當(dāng)然郑临,引入了 High Water Mark 機(jī)制栖博,會導(dǎo)致 Broker 間的消息復(fù)制因?yàn)槟承┰蜃兟岫矗敲聪⒌竭_(dá)消費(fèi)者的時(shí)間也會隨之變長(因?yàn)槲覀儠鹊却?fù)制完畢)仇让。延遲時(shí)間可以通過參數(shù) replica.lag.time.max.ms 參數(shù)配置躺翻,它指定了副本在復(fù)制消息時(shí)可被允許的最大延遲時(shí)間。