問題現象
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 發出去的那一刻,就不再是單執行緒的世界了。
設計原則
- 先決條件先完成:確保依賴的資料都已就緒
- 冪等性設計:重複執行不會造成錯誤
- 順序依賴明確化:用程式碼明確表達順序關係
發佈留言