CDC 把資料庫的每一筆變更(insert/update/delete)擷取成一條有序的事件流。核心觀念是:讓某個資料庫成為唯一的 source of truth,所有其他系統(搜尋索引、快取、倉儲)都只是這條變更流的衍生狀態。
🧠 Intuition
TIP
不要在應用層「雙寫」——寫完 DB 又寫一次 ES。改成:只寫 DB,然後讀 DB 的 replication log,把變更廣播出去。replication log 本來就是給 follower 用的,CDC 只是多接一個 consumer。
- DB 內部本來就有一份有序的變更日誌(MySQL binlog、Postgres WAL/logical replication)給複製用。
- CDC 把這份 log 解析成乾淨的事件流,當成像 Kafka follower 一樣的下游。
- 下游各自重放這條流,重建自己需要的形態:反正規化文件給搜尋、聚合給報表、扁平結構給快取。
- 因為變更有序且完整,任何下游都能從頭重放、保證最終與 DB 一致。
⚖️ Tradeoffs
為什麼不用應用層雙寫 (dual write)
- 雙寫=應用同時寫 DB 和 ES。問題是兩次寫入沒有原子性:一個成功一個失敗,兩邊就永久分歧。
- 還有並發競態:兩個請求對同一筆的兩次寫入,到達 DB 和 ES 的順序可能相反,最後狀態不一致。
- CDC 從根本避開:只有一個寫入點(DB),順序由 replication log 唯一決定,下游照序重放永遠收斂。
初始快照 (snapshot) + 增量 (log)
- replication log 通常只保留近期(會被截斷),無法回溯到資料庫誕生。
- 新接一個 consumer(如新建索引)要先做一次一致性快照,記下對應的 log 位置 (LSN/GTID),再從該位置接增量。
- Debezium 等工具把「快照 → 無縫切換到增量」自動化,是 CDC 上線最容易出錯的環節。
Log compaction — 讓流也能當完整資料集
- 一般 log 會截斷舊紀錄,重放不到全貌。log compaction(Kafka)只保留每個 key 的最新值,刪除被覆蓋的舊版。
- 效果:壓實後的 log 等價於「整個資料庫的當前快照」,新 consumer 可直接從頭重放重建完整狀態,不必另做 snapshot。
- delete 用 tombstone(value 為 null)表示,compaction 後該 key 徹底消失。
CDC vs Event Sourcing
- 兩者都「以不可變事件流為中心」,但抽象層次不同。
- CDC:事件是低階的資料庫狀態變更(這列被改成這樣),由 DB 自動產生,應用無感。
- Event Sourcing:事件是高階的領域意圖(「使用者取消了訂單」),由應用主動寫入,當前狀態是事件重播的結果。
- 取捨:CDC 改造成本低(接 binlog 即可)但事件語意貧乏;event sourcing 表達力強、可重建任意視圖,但要重新設計寫入模型。
🔑 Takeaways
- CDC 把 DB 的 replication log 變成一條變更流,讓 DB 當 source of truth,搜尋索引/快取/倉儲都只是衍生狀態。
- 它從根本解掉雙寫的原子性與順序問題:只有一個寫入點,下游照序重放必然收斂。
- 上線兩件事要做對:初始快照 + 從正確 LSN 接增量;想讓流自帶完整資料集就用 log compaction。
- CDC 是低階、DB 產生的狀態變更;event sourcing 是高階、應用產生的領域意圖——同源思想、不同抽象。Debezium 是事實上的 CDC 標準工具。
2026-07-01
- Q: CDC 和 event sourcing 都在講「不可變事件流」,到底差在哪?
- A: 差在事件的語意層次與誰產生它。CDC 的事件是資料庫替你產生的低階變更——「orders 表第 42 列的 status 欄從 ‘paid’ 變成 ‘cancelled’」,它不知道業務為什麼這樣改,只忠實記錄結果;好處是接上 binlog 就有、應用零改動。Event sourcing 的事件是應用主動寫的高階意圖——「OrderCancelled,原因:客戶要求」,當前狀態是把這些事件重播算出來的;好處是事件保留了「為什麼」,能重建任何你事後才想要的視圖,但代價是整個寫入路徑要圍繞事件重新設計。一句話:CDC 是把既有 DB 的副產物拿來用,event sourcing 是把事件當成唯一的真相來源從頭設計。