分散式串流平台 Kafka (Kafka)

一句話定位

Kafka 是分散式、append-only 的 commit log,可以同時當「能水平擴展的 message queue」和「能重播的 event stream」用。
核心三件事:① Partition 是擴展與順序的單位(同一 key 進同一 partition、partition 內有序);② Consumer Group 決定每則訊息被處理幾次(一個 partition 只給組內一個 consumer);③ Replication + acks 決定持久化保證。面試一句總結:Kafka is always available, sometimes consistent(CAP 中偏 AP)

Kafka 是什麼

Apache Kafka 是開源、分散式的 event streaming 平台。它的強項是高吞吐、可水平擴展、持久化,在設定得當(replication + acknowledgment)時對訊息遺失提供很強的保證。

它跨越了兩個角色,差別其實不大 —— 消費模式不同而已:

用法 消費語意 典型場景
Message Queue 每則訊息由 group 裡一個 consumer 處理後即算「被消費」 非同步任務解耦、削峰、保證順序
Event Stream log 保留可重播,多個 consumer group 各自獨立讀同一份資料 即時彙總、pub/sub、多下游消費

兩種模式下 consumer 都用 offset commit 記錄進度;差別只在「訊息被讀走後會不會留下」與「能不能多組重複消費」。


核心術語(由上而下)

Kafka Cluster
├── Broker 1 ──┐
├── Broker 2   │  每台 broker = 一台伺服器,存資料 + 服務 client
└── Broker 3 ──┘  broker 越多 → 能存更多資料、服務更多 client

Topic "soccer"  ← 邏輯分組,publish/subscribe 的單位
 ├── Partition 0  → [m0][m1][m2][m3] ...   append-only log(實體分組)
 ├── Partition 1  → [m0][m1][m2] ...       每則訊息有唯一遞增 offset
 └── Partition 2  → [m0][m1] ...           不同 partition 可在不同 broker

Producer ──寫──> Topic                 Consumer ──pull──> Partition
名詞 是什麼 一句記憶
Broker 一台伺服器(實體/虛擬),存資料並服務 client 加 broker = 加容量
Topic 訊息的邏輯分組;publish/subscribe 的對象 用來「組織」資料
Partition topic 底下的實體分片,append-only log 用來「scale」與「保證順序」資料
Offset 訊息在某 partition 內的唯一遞增序號 consumer 用它記「讀到哪」
Producer 把訊息寫進 topic 的程式 多 producer 可寫同一 topic
Consumer 從 topic 讀訊息的程式(pull 模式) 自己控制消費速率
Consumer Group 一群 consumer 協作消費一個 topic;每 partition 只給組內一個 決定「每則訊息處理幾次」
Controller cluster 內負責管理 metadata、leader 指派與 replication 的角色 broker 掛了它負責 failover
Topic vs Partition 是常見追問

Topic 是「邏輯」分組,partition 是「實體」分組。 一個 topic 可以有多個 partition,散落在不同 broker 上。Topic 用來組織資料,partition 用來擴展資料與保證順序


為什麼需要 Partition:擴展 + 順序

用世足賽即時數據網站當例子(進球、吃牌、換人都要即時更新):

單一 queue + 單一 consumer:
  事件暴增 → queue 撐不住,consumer 像被消防水柱灌一樣被壓垮 ❌

水平擴展但「隨機」分散事件:
  比賽還沒開始就出現進球、球員還沒犯規就被記牌(順序全亂)❌

依「哪一場比賽」當 key 分配:
  同一場比賽的事件 → 同一 partition → 天然按 offset 順序處理 ✅

這就是 Kafka 的核心取捨:順序保證只存在於單一 partition 之內,不是整個 topic 之內。 想保證某類事件有序,就用對的 key 把它們導進同一個 partition。

Partition 內有序 ≠ Topic 內有序

Kafka 不保證跨 partition 的全域順序。若你需要「整個 topic 嚴格有序」,唯一辦法是只用 1 個 partition —— 但那等於放棄並行度。實務上是「分對 key」讓需要有序的那一群落在同一 partition。


訊息結構與 partition 決定流程

一則訊息(record)有四個欄位,技術上都可不填:

欄位 用途
value payload,真正的資料
key 決定進哪個 partition(hash),是順序與分布的關鍵
timestamp 建立/寫入時間(注意:partition 內順序由 offset 定,非 timestamp)
headers 類似 HTTP headers 的 key-value metadata(可放 trace id)

訊息 publish 時分兩步定位:

1. Partition Determination(決定 partition)
   partition = hash(key) % num_partitions      ← 預設 hash 用 murmur2
   - 有 key:同一 key → 同一 partition(partition 內保證順序)
   - 無 key:現代 client 用 "sticky" partitioner(先集中一 partition 再輪替,利批次效率)

2. Broker Assignment(對應 broker)
   依 controller 維護的 cluster metadata 找出該 partition 的 leader 在哪台 broker
   → producer 直接把訊息送給那台 broker
Key 的選擇是面試 scaling 的核心

partition = hash(key) % num_partitions。選錯 key → hot partition(流量集中);選對 key → 資料均勻分布、並行度最大化。討論 scaling 時,從 partition strategy 談起就對了


Append-only Log:為什麼這樣設計

每個 partition 本質上是一支 append-only 的 log 檔,訊息只往尾端追加,所以 Kafka 常被稱為 distributed commit log

好處 原因
Immutability 不可變 寫入後不就地修改,只依 retention / log compaction 刪除;簡化 replication、加速 recovery、避開可改寫系統的一致性問題
Efficiency 效能 只做尾端追加 → 減少磁碟 seek(seek 通常是儲存瓶頸),可順序寫入
Scalability 可擴展 簡單模型利於水平擴展:加 partition、分散到多 broker、複製到多 broker 提升容錯

這與 04-Database-Indexing/03-LSM-Tree 的「append-only / 順序寫入避開 seek」是同一套效能哲學。


Offset 與 Consumer Group:每則訊息處理幾次

Topic "orders" (3 partitions)        Consumer Group "billing"
 ┌── P0 ──────────────┐  ───────────►  Consumer A   (讀 P0)
 ├── P1 ──────────────┤  ───────────►  Consumer B   (讀 P1, P2)
 └── P2 ──────────────┘  ───────────►  (若加 Consumer C → rebalance 分 P2 給它)

規則:一個 partition 在「一個 group 內」只會分給「一個 consumer」
     → 正常情況每則訊息只被組內一個 consumer 處理一次
     → consumer 數量 > partition 數量時,多出來的 consumer 會閒置
Kafka 預設是 at-least-once(最常被深挖的可靠性點)

若 consumer 在處理完訊息後、commit offset 前掛掉,重啟後該訊息會再被處理一次

  • 想要 exactly-once 需額外設定:idempotent producer + transactional API,或讓 consumer 端的處理冪等(idempotent)
  • 故障情境下 Kafka 仍不會把同一訊息拆給多個 consumer —— 它只會重送,不會重分。
  • 設計取捨:commit 越晚越安全(重做成本低),但重複機率越高。把 consumer 單次工作量壓到最小(見 Web Crawler 拆兩階段)。

Replication:Leader-Follower 與持久化保證

訊息寫入 partition 後,Kafka 靠 replication 保證持久與可用,採 leader-follower 模型:

Partition 0:
  Broker 1: [Leader]    ← producer 寫入 / consumer 預設讀(2.4+ 可從 follower 讀降延遲)
  Broker 2: [Follower]  ← 被動從 leader 複製(in-sync replica, ISR)
  Broker 3: [Follower]  ← 被動從 leader 複製

Leader 掛掉 → controller 把某個 in-sync follower 提升為新 leader → partition 續活
角色 / 設定 說明
Leader replica 每 partition 一個,負責寫入(與預設讀取);由 controller 均衡分散到各 broker
Follower replica 不服務 client,被動複製,當備援;leader 掛時可接手
ISR(in-sync replicas) 已跟上 leader 的 replica 集合;只有 ISR 內的能被提升為 leader
replication factor 每 partition 有幾份 replica,常見 3(1 leader + 2 follower)

Producer 的 acks 設定決定持久化強度:

acks 值 行為 取捨
acks=0 送出就不管 最快、可能丟訊息
acks=1 leader 寫入就回覆 折衷;leader 在複製前掛會丟
acks=all 所有 ISR 都收到才回覆 持久化最強、延遲最高

Pull-based 消費模型

Kafka 的 consumer 是**主動向 broker 拉(pull)**新訊息,間隔由自己控制,而非 broker push。官方說這是刻意設計:

對比 01-Networking/05-Realtime-Protocols 的 push(SSE/WebSocket):Kafka 選 pull 換取背壓(backpressure)天然由 consumer 控制,這正是 12-Ops-and-Reliability/02-Overload-Protection 想要的特性。


Scalability:先估算,再決定要不要談

面試順序:先估吞吐量,再決定要不要 scaling

單一 broker 量級參考(依訊息大小與硬體變動,僅數量級):約可存 1TB 資料、每秒處理約 100 萬則訊息。設計若不超過這個量,可能根本不必談 scaling

擴展策略:

策略 做法
加 broker(水平擴展) 最直接;但要確保 topic 有足夠 partition,否則新 broker 沒事做
Partitioning Strategy 面試 scaling 的重點:選對 message key 讓資料均勻分布(managed 服務常動態處理底層,但概念要懂)
以 topic 為單位討論 不同 topic 需求不同:高吞吐 topic 多 partition、低吞吐 topic 單 broker 即可;擴展某 topic = 增加其 partition 數
Anti-pattern:把大塊資料塞進 Kafka

Kafka 不是資料庫、也不是存大檔的地方,它存小訊息供快速處理(單則建議 < 1MB,message.max.bytes 雖可調但別硬塞)。
設計 YouTube 時,不要把影片丟進 Kafka 讓 worker 拉。正確做法:影片存 07-Caching-Storage/02-Blob-Storage(S3),Kafka 裡只放一則「指向 S3 位置」的小訊息,worker 拿訊息去處理。


Hot Partition:面試官最愛問

例:Ad Click Aggregator 用 ad id 當 partition key。當 Nike 推出 LeBron 新廣告,那個 partition 爆量 → hot partition

ad_id = "nike_lebron"  → hash → 永遠同一個 partition → 該 partition 過載 🔥
                                  其他 partition 閒置
解法 做法 / 取捨
1. No key(預設分散) 不給 key,Kafka sticky 分散到各 partition;犧牲順序,順序不重要時的好選項
2. Random salting ad id 加隨機數/時間戳當 key → 負載均勻,但 consumer 端彙總邏輯變複雜
3. Compound key ad id + 另一屬性(地區 / user id 區段)組合 → 更均勻,尤其找得到獨立變化的屬性時
4. Back pressure 讓 producer 減速:自建 cluster 可檢查 partition lag,過高就減速;managed 服務常內建

可靠性:Retry / DLQ / Performance


面試話術

不要只說「我用 Kafka」

而是說清楚「我為什麼用 Kafka、用在哪一段、key 怎麼選、順序與重複怎麼處理」。

何時在設計裡加 Kafka:

你想要的 Kafka 怎麼幫 範例題
非同步處理工作 先回標準畫質,把轉檔任務丟 topic,系統有空再做 YouTube 上傳轉檔
保證按順序處理 用對的 key 讓相關事件進同一 partition Ticketmaster 虛擬排隊
producer/consumer 解耦擴展 兩端各自擴展,避免一個服務拖垮另一個 Microservices 解耦
連續即時處理(stream) consumer 持續即時彙總新進資料 Ad Click Aggregator
一份資料給多個下游 多個 consumer group 各自獨立讀(pub/sub) FB Live Comments

收尾金句:


自我測驗重點

問題 重點
Topic vs Partition 邏輯分組 vs 實體分片;組織資料 vs 擴展+順序
順序保證的範圍 只在單一 partition 內,非整個 topic
partition 公式 hash(key) % num_partitions(murmur2);選錯 key → hot partition
Consumer Group 規則 一個 partition 組內只給一個 consumer;多 consumer 可閒置
預設消費語意 at-least-once;exactly-once 需 idempotent + transactional
acks 三檔 0 / 1 / all;acks=all 等所有 ISR,最強
單 broker 量級 ~1TB、~100 萬則/秒(數量級),不超過可能不必談 scaling
Hot partition 解法 no key / salting / compound key / back pressure
Anti-pattern 把大檔塞 Kafka;應存 S3,topic 只放指標
Consumer 重試 Kafka 無內建(SQS 有);自建 retry topic + DLQ
CAP 定位 AP(always available, sometimes consistent)