Kafka 實務坑筆記(五):多執行緒下的順序問題

問題現象

30 張訂單都成功取號了,但是:

  • 部分訂單沒有配送單文件
  • 裝箱單也缺少這些訂單

明明取號成功,為什麼後續文件會缺失?

問題分析

工作流程

拆訂單 → 產生工作單們 → 打入 Kafka
                          ↓
              ┌───────────┴───────────┐
              ↓                       ↓
         存入資料庫              執行取號(Queue)
              ↓                       ↓
         更新欄位A              更新欄位A

問題根源

「存入資料庫」和「執行取號」是並行執行的!

  • 正常情況:Queue 卡住時,資料庫先寫完,沒問題
  • 異常情況:Queue 執行很快,取號完成時資料庫還沒寫完

導致後續檢查 Job 找不到應有的資料。

這不只是 Kafka 的問題

這是經典的多執行緒競爭條件(Race Condition)問題:

  • 雖然每個 Job 是單執行緒
  • 但打出 Queue 後,就進入多執行緒的世界
  • 必須用多執行緒的思維來設計

解決方案

方案一:確保前置條件完成

// 先完成資料庫寫入
await saveToDatabase(order);

// 確認寫入成功後,再發送 Queue
await sendToKafka(orderQueue);

方案二:在 Consumer 端檢查

// Consumer 處理前檢查資料是否就緒
if (!isDataReady(orderId)) {
    // 重新放回 Queue 稍後處理
    retry(message);
    return;
}
process(message);

方案三:使用事務

@Transactional
public void processOrder(Order order) {
    saveToDatabase(order);
    sendToKafka(orderQueue); // 事務提交後才真正發送
}

關鍵學習

偶發性的問題,要混合多執行緒思維來分析。Queue 發出去的那一刻,就不再是單執行緒的世界了。

設計原則

  1. 先決條件先完成:確保依賴的資料都已就緒
  2. 冪等性設計:重複執行不會造成錯誤
  3. 順序依賴明確化:用程式碼明確表達順序關係

留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *