現實資料多是無界的——今天、明天持續產生。批次處理得人為切時間段、延遲數小時。串流處理放棄固定切片,改為事件一發生就處理。核心單位是 event:小型、自包含、不可變、帶時間戳的事實。
🧠 Core Ideas
TIP
訊息代理分兩派:傳統代理(AMQP/JMS) 投遞即刪、消費是破壞性的;日誌式代理(Kafka/Kinesis) 把訊息追加到可分區的日誌、consumer 循序讀 offset、讀取不刪。後者天然支援扇出與重讀歷史,是串流處理的地基。
- 傳統代理適合任務佇列式的非同步 RPC;日誌式適合需保序、可回放的串流。
- 多 consumer 兩模式:負載平衡(每則只給一個)與扇出(每則給所有);日誌式靠「整個 partition 分給一個 consumer」做負載平衡,所以 consumer 數 ≤ partition 數。
⚖️ 資料庫即串流:CDC 與事件溯源
雙重寫入的陷阱 → 單一 leader
- 想同時更新 DB、快取、搜尋索引,直接雙寫會有競態(交錯導致永久不一致)與部分失敗。
- 正解:讓一個系統當唯一 leader,其他從它衍生。
Change Data Capture (CDC)
- 觀察 DB 所有寫入變更、提取成可複製的串流;本質是讓 DB 當 leader、其他系統當 follower。
- 穩健做法是解析複製日誌(如 Debezium 讀 MySQL binlog);日誌式代理很適合傳這些變更(保序)。日誌壓縮只留每個 key 最新值,能讓新衍生系統從 offset 0 重建。
Event Sourcing
- 應用邏輯建構在不可變事件日誌上,事件反映使用者意圖(「取消選課」而非「刪一列」),語意更豐富、便於日後衍生新視圖。
- 當前狀態 = 事件日誌的確定性重放;因事件不互相覆蓋,需完整歷史重建、日誌壓縮不適用,實務用 snapshot 加速。
- 命令 vs 事件:請求先是可被拒的 command,驗證通過才變成不可撤回的 event——驗證要在轉換前同步完成。
⚖️ 處理串流的三個難點
時間:事件時間 vs 處理時間
- 用處理時間開視窗,遇到積壓/重啟會製造虛假尖峰(實際頻率其實平穩)。
- 用事件時間才對,但永遠無法確定某視窗事件是否收齊——落後者 (straggler) 要嘛忽略(監控丟棄比例)、要嘛發修正值。
- 視窗類型:tumbling(不重疊)、hopping(固定重疊)、sliding(滑動)、session(依活動間隔)。
Stream Join 三型
- stream-stream(視窗 join,如搜尋↔點擊);stream-table(用 CDC 同步的本地副本增強事件);table-table(維護兩 changelog 的物化視圖,如 Twitter 時間軸)。
- 都要維護狀態、順序重要;跨分區順序不定會讓 join 非確定性。
容錯:讓輸出看似 exactly-once
- 微批次(Spark Streaming)/檢查點(Flink)在框架內給 exactly-once。
- WARNING:一旦輸出離開框架(寫外部 DB、寄信),重試會讓副作用重複執行。
- 解法:原子提交(框架內把所有輸出與 offset 一起生效)或冪等(附 Kafka offset 判斷是否已套用)。
🔑 Takeaways
- 無界資料 → 事件一到就處理;日誌式代理(可回放、保序)是串流處理的地基。
- DB 寫入可視為串流:CDC(從底層提取)與 Event Sourcing(從應用意圖出發)讓多系統從單一事實衍生。
- 三大難點:時間(事件時間 vs 處理時間 + 落後者)、join(三型皆需狀態)、容錯(原子提交或冪等才能真 exactly-once)。
No notes yet — jot your takeaways or Q&A here.