問題現象
用戶下訂單後要出貨,但系統竟然發動了兩次一模一樣的取號 Job,導致:
- 一個取號成功
- 一個取號失敗
- 用戶體驗極差
問題分析
背景條件
- 巡邏檢查每 3 秒打一次 Queue
- 正式環境有兩台 Job 機
- Queue 與 Queue 之間沒有其他 Queue 插入
發生了什麼?
當沒有指定 Partition Key 時,Kafka 會自動將訊息分散到不同的 Partition。這導致:
- 同一張訂單的 Queue 被分到不同 Partition
- 兩台機器同時消化到相同訂單的 Queue
- 兩台同時去取號 → 碰撞發生
解決方案比較
| 方案 | 做法 | 優點 | 缺點 |
|---|---|---|---|
| 快取 + Lock | 處理前檢查鎖 | 不改架構 | 極限情況仍可能碰撞,且拖慢速度 |
| 指定 Partition Key | 用訂單 ID 當 Key | 確保順序 | 可能造成熱點 Partition |
Partition Key 設計原則
不設定 Key(null)
- Kafka 自動分散到各 Partition
- Consumer 程式必須是無狀態的
- 適合:獨立任務、無順序要求
設定 Key
- 相同 Key 的訊息會進入同一個 Partition
- 確保順序性
- Consumer 必須處理有序邏輯
- 適合:有狀態的任務、需要順序保證
最佳實踐
選擇合適的 Partition Key
// 好的選擇:訂單 ID(確保同一訂單順序處理)
producer.send(new ProducerRecord(topic, orderId, message));
// 避免:用戶 ID(可能造成熱點)
// 大戶用戶的訂單會集中在同一個 Partition
考慮因素
- 業務邏輯:是否需要順序保證?
- 分布均勻度:Key 的基數是否足夠大?
- 熱點風險:是否有超大 Key 導致傾斜?
Partition Key 的設計直接影響系統的正確性和性能,務必謹慎選擇。
發佈留言