Kafka生產消費資料丟失和優化小結

削土豆的人er發表於2020-10-14

我們經常會遇到kafka資料丟失的問題,所以將遇到過的或有可能造成資料丟失的問題進行個小總結。

其實在kafka處理資料的流程有很多,把這些流程梳理一遍,有助於分析資料丟失的情況,從這個圖中可以看出資料流向,圖中涉及的所以過程都可能造成資料的丟失。
在這裡插入圖片描述

  1. 首先要確定是否有業務資料寫入
  2. 再明確資料是在kafka之前就已經丟失還是消費端丟失資料的?
    2.1 如果是在寫入端丟失資料,那麼每次結果應該完全一樣(在寫入端沒有問題的前提下)。
    2.2 如果是在消費端丟失資料,那麼換個消費group重新消費,多次消費結果完全一模一樣的機率很低。

生產階段

丟失問題:若生產時是同步模式,那麼訊息一旦生產,就會阻塞到直到收到server端的確認。但非同步模式下,訊息不會立刻server端,而是在客戶端的緩衝區中進行快取,快取到指定大小或指定時間後,再傳送給server。
所以,若在非同步模式下,業務已經生產了資料,但還沒來得及傳送給server端時,server端就crash了,並且在重傳週期內,server端一直未恢復,那麼此訊息就會丟失。
效能問題:選擇同步模式,雖然很穩妥,但是每次都需要leader再向客戶端傳送確認。這個會降低傳送速度。但選用非同步模式,雖然速度是加快了,但是無法保證準確到達,快取的越多,到達時間會變的久一些,並且快取越大,傳送也可能會變更慢。
優化:在producer端有幾個引數會控制傳送大小和重傳的引數。

  • batch.num.messages:每次批量傳送給每個paritition訊息的數量(只對 asyc 起作用)
  • request.required.acks:0 表示 producer 不需要等待 leader 的確認,1 代表需要 leader 確認寫入它的本地 log 並立即確認,-1 代表所有的備份都完成後確認(只對 async 起作用)
  • queue.buffering.max.ms,預設值:5000,在 producer queue 的快取的資料最大時間(只對 async 起作用)
  • queue.buffering.max.message,預設值:10000,producer 快取的訊息的最大數量(只對 async 起作用)
  • queue.enqueue.timeout.ms,預設值:-1,0 當 queue 滿時丟掉,負值是 queue 滿時 block, 正值是 queue 滿時 block 相應的時間(只對 async 起作用)
  • message.send.max.retries,預設值:3,訊息傳送最大嘗試次數。
  • retry.backoff.ms,預設值:300,每次嘗試增加的額外的間隔時間。這個引數不建議在生產環境設的如此小。

broker處

  1. 即使kafka收到了訊息,仍然可能丟失,因為kafka收到訊息後,並不是立刻落盤,而是存在了快取中,若在此階段kafka異常或磁碟壞掉,那麼此訊息仍會丟失。
    解決:修改kafka的配置引數,調整flush到檔案的時間和條數
    log.flush.interval.messages=10000
    log.flush.interval.ms=3000
  2. 單批資料的長度超過kafka的限制時,會丟失資料,報kafka.common.MessageSizeTooLargeException異常。
    message.max.bytes=20000000(broker能接收訊息的最大位元組數,這個值應該比消費端的fetch.message.max.bytes更小才對,否則broker就會因為消費端無法使用這個訊息而掛起)
    fetch.message.max.bytes=20485760
    這條配置要符合 client的生產大小<server端可接受的大小<server端可傳送的大小<client的消費大小

消費邏輯

  1. 在此階段,會批量從server端讀取資料,如果設定自動提交位移,那麼有可能存在還未被業務側讀取,但offset已更新的情況,那麼資料就會丟失:
    enable.auto.commit:是否自動提交位移,如果為false,則需要在程式中手動提交位移。
  2. 關於效能優化
  • 啟動的消費執行緒數
  • fetch.max.bytes:單次拉取資料的最大位元組數量
  • metadata_max_age_ms:強制進行metadata重新整理的週期
  • max.poll.records:單次 poll 呼叫返回的最大訊息數,如果處理邏輯很輕量,可以適當提高該值。但是max.poll.records條資料需要在 metadata_max_age_ms這個時間內處理完

總結如下:
在考慮效能問題時,根據資料的特點和要求,需要考慮:

  1. 是單程式還是多程式生產
  2. 是同步還是非同步生產,是否需要全部副本都確認
  3. 調整batch_size的大小,適當增大batch 大小可以來減小網路IO和磁碟IO的請求
  4. 是否需要多個partiton,分割槽是kafka進行並行讀寫的單位,是提升kafka速度的關鍵。
  5. 是否需要幾個副本,副本越多,代價就是需要更多資源,尤其是磁碟資源,需要在副本數和可靠性之間平衡
  6. 是否需要開啟壓縮
  7. 消費時,是否需要多程式消費

Rebalance問題
在專案中時常遇到rebalance問題,單獨小結一下:
觸發rebalance的條件有三種:

  1. 組成員發生變更(新 Consumer 加入組、已有 Consumer 主動離開組或已有 Consumer 崩潰了)
  2. 訂閱主題數發生變更
  3. 訂閱主題的分割槽數發生變更
    我們常遇到的就是consumer沒有和server沒有保持活躍,導致server認為此consumer已退出,所以需要rebalance。而此時offset還未提交,所以會有重複消費的問題。
    而沒有保持活躍的原因有多種:
    1)處理的執行緒被kill
    2)消費者消費太慢,導致超時
    3)消費太快,導致消費者處於空poll的狀態,阻塞傳送心跳執行緒;
    以上原因都會讓server認為需要rebalance。
    優化方案一個是:降低max_poll_records(預設500)或提高metadata_max_age_ms(預設5分鐘強制重新整理metadata)
    另一個解決方案是修改heartbeat_interval_ms(預設3秒)和metadata_max_age_ms(預設5分鐘)為差不多的大小,例如將metadata_max_age_ms修改為3s

相關文章