Kafka經典三大問:數據有序丟失重復
在kafka中有三個經典的問題:
- 如何保證數據有序性
- 如何解決數據丟失問題
- 如何處理數據重復消費
這些不光是面試常客,更是日常使用過程中會遇到的幾個問題,下面分別記錄一下產生的原因以及如何解決。
1. 消息有序
kafka 的數據,在同一個partition下是默認有序的,但在多個partition中並不一定能夠保證其順序性。kafka因為其自身的性質,適合高吞吐的流式大數據,對數據有序性要求不嚴格的場景比較適用。
1.1. 為什么只保證單partition有序?
如果Kafka要保證多個partition有序,不僅broker保存的數據要保持順序,消費時也要按序消費。假設partition1堵了,為了有序,那partition2以及后續的分區也不能被消費,這種情況下,Kafka 就退化成了單一隊列,毫無並發性可言,極大降低系統性能。因此Kafka使用多partition的概念,並且只保證單partition有序。這樣不同partiiton之間不會干擾對方。
1.2. 解決方式
kafka自身沒有提供整個topic級別的消息順序性,但我們可以在業務層面來處理。
可以通過message key來保證你需要保持順序性的數據發送到同一個partition,即send方法,可以指定三個參數(topic, partition, key), partition和key是可選的,如果指定了同一個partition的話,那么數據就是有序的。
同時在消費端,只創建一個消費者來消費topic,但后續的話這個消費者可以寫入N個內存隊列,保證具有相同key的數據寫入同一個內存隊列即可。引入內存隊列是為了解決業務處理單線程處理太慢的問題,多個內存隊列可以起多個線程進行消費,同時具有相同key的數據在同一個內存隊列中,這樣就能保證順序性。

2. 數據丟失
丟失數據一般分為兩種情況:mq自己弄丟了,業務處理弄丟了。
2.1. kafka弄丟了數據
kafka的某個broker宕機,重新選舉partition的leader時,如果其他的follower還沒有完成數據同步,此時leader掛了,那么就有可能造成數據丟失的問題。
kafka提供了幾個參數來保證數據不丟失:
-
replication.factor:分區副本數,最低設置為2,即要求每個Partition至少擁有兩個副本; -
min.insync.replicas:要求一個leader感知到有至少一個follower還跟自己保持聯系,沒掉隊,這樣才能確保leader掛了還有一個follower; -
acks:在producer發送數據成功后,kafka會給生產者返回一個ack信息,這個稱為kafka的ack機制;ack有3個可選值,-1、0、1;
-
ack=1:producer只要收到一個分區副本寫入成功的通知就認為推送消息成功,這個分區副本特指leader副本;
-
ack=0:producer發送一次就不再發送了,不管是否發送成功;
-
ack=-1:producer只有收到分區內所有副本的成功寫入通知才算推送消息成功;
-
-
retries:重試次數,如果寫入失敗就會進行重試,直到超過retries設置的值;
2.2. 消費端弄丟了數據
例如消費者已經獲取到這個數據,並且提交了offset,但后續在對數據進行業務操作的時候掛掉了,導致數據沒有成功處理,這時候kafka認為你已經成功獲取了,但實際沒有,就造成了數據丟失的問題。
一般情況下用手動提交的方式來解決,當數據處理成功后再提交offset。
3. 重復消費
在ack=1的情況下,是有可能存在消息丟失的情況的,因為producer收到leader寫入成功的通知就認為推送成功,但實際上leader副本在把消息同步到follower副本的時候失敗了,這時候消息就丟失了。
為了處理這種推送失敗的情況,kakfa引入了回調機制來處理,實際上就是一種重試的方式,這時候會出現因為重試機制導致消息亂序的情況。
3.1. 解決重試機制引起的消息亂序
生產者Producer
為了實現producer的冪等性,kafka引入了Producer ID和Sequence Number兩個參數,對於每個生產者,該Producer發送的消息都對應一個單調增的Sequence Number。同樣的Broker端也會為每個生產者的每條消息維護一個序號,並且每commit一條數據時就會將其序號遞增。
對於接收到的數據,如果其序號比Borker維護的序號大一(即表示是下一條數據),Broker會接收它,否則將其丟棄。
如果消息序號比Broker維護的序號差值比一大,說明中間有數據尚未寫入,即亂序,此時Broker拒絕該消息,Producer拋出InvalidSequenceNumber
如果消息序號小於等於Broker維護的序號,說明該消息已被保存,即為重復消息,Broker直接丟棄該消息,Producer拋出DuplicateSequenceNumber
Sender發送失敗后會重試,這樣可以保證每個消息都被發送到broker
消費者Consumer
同樣的也是利用冪等性的原理來解決,可以給每條數據加上一個唯一標識,進行數據處理的時候校驗這個標識是否存在,如果存在即為重復數據,丟棄。
