數據中台 CDC 開了,資料就會同步嗎?三種做法的機制,以及 Oracle 與 Informix 的現實選項
CDC 啟用了,資料就會正確同步嗎?這篇拆開三種做法的機制與代價——查詢法、觸發器法、讀日誌,看開源工具在表沒有主鍵時的實際行為,並把 Oracle、SQL Server、PostgreSQL、MySQL、MariaDB 與 Informix 六家的原生 CDC 攤開來比。
三種做法的機制,以及 Oracle 與 Informix 的現實選項
AI 讓資料需求變了:要更即時、要更多來源、要更細的顆粒度。
過去的做法是每天晚上跑一次批次,把資料搬進資料倉儲。這個節奏現在不夠用;而為了不影響線上資料庫的效能,也不能單純把批次跑得更密。
於是方向轉成另一種:來源資料庫一有變動,資料倉儲就跟著改。這種技術叫 CDC(Change Data Capture,變更資料擷取)。
很多人以為CDC 啟用了,資料就會同步,但這句話是否成立,要根據資料現況架構來決定。
我自己也是為這件事思考了許久,才漸漸逐漸清晰,因此就把目前得到的想法,寫下來,跟大家一起交流。
CDC 確實會把來源系統裡發生的每一筆變更讀出來,一筆不漏。但「讀出來」跟「正確地放進目標端」是兩件事,中間隔著一個問題:這筆變更,該套用到目標端的哪一列?
回答這個問題的東西,就是資料庫的鍵(Key)。
這一篇先講前半段——CDC 怎麼把變更讀出來,三種做法各自的機制與代價,開源工具選了哪一種,以及 Oracle 和 Informix 這兩套在台灣核心系統裡最常見的資料庫,各自給了什麼選項。
一、三種做法,三個攔截點
CDC 的全名是 Change Data Capture,中文常翻成「變更資料擷取」。它要做的事一句話講得完:找出來源資料庫裡哪些資料變了,把變更送到另一個地方。1
差別在於,你要站在哪裡取得資料變了的資訊。

二、查詢法:站在表的外面,定時去問
這個做法你可能已經很熟了,只是它在你那裡叫別的名字。如果你聽過、或自己做過「每天晚上跑批次,把有異動的資料抓進資料倉儲」——那就是這一種。
不過有兩個容易混在一起的軸要分開:「批次還是即時」講的是節奏,「怎麼知道哪些變了」講的是方法。
- 每晚把整張表重抓一次 → 那不是 CDC,是全量重載
- 每晚只抓有異動的那些 → 這才是查詢法
- 即時同步 → 通常指的是日誌法
同一種方法也可以跑不同節奏:查詢法每分鐘跑一次,就很接近即時;日誌法累積一批再套用,也可以做成批次。所以下次聽到「我們是批次」「我們要即時」,先問一句:那你怎麼知道哪些資料變了?
最直覺的做法。在來源表上找一個「最後更新時間」的欄位,定時去撈:
SELECT * FROM 主檔
WHERE last_upd_time > '上次抓到這裡的時間';
每五分鐘跑一次,把這五分鐘內動過的列撈出來。只要那個欄位已經存在,就不用改資料庫、不用特殊權限,寫個排程就能上線——這是它唯一但很重要的優點。
它抓不到三種東西:
第一,刪除。 一筆資料被刪掉之後,它不會留下一列告訴你「我被刪了」。你的查詢只看得到還在的資料,看不到消失的。想抓到刪除,只剩一條路:把整張表撈回來跟目標端比對——那已經不叫 CDC 了。
第二,中間狀態。 五分鐘抓一次,如果這五分鐘內同一筆資料改了三次,你只會看到最後一次的樣子。對「現在長什麼樣」的需求來說這沒差,對「這筆資料的變化過程」就全丟了。
第三,沒有時間欄位的表。 這是最現實的一個。跑了幾十年的系統裡,很多表根本沒有 last_upd_time。
就算有,還有一個更麻煩的問題:那個欄位是應用程式在維護的,不是資料庫保證的。 只要有人用 SQL 直接改了資料——維運修個資料、批次程式跑一段更新——那個欄位就不會動,而你的 CDC 永遠不會知道這筆資料變過。
換句話說,查詢法的正確性靠的是紀律,不是結構。
順帶一提:如果你現在用排程工具做增量抽取——每天定時跑一段 WHERE last_upd > 上次的水位線(也就是上次抓到哪個時間點)——那就是查詢法。你已經在做 CDC 了,只是沒人這樣叫它。
差別不在機制,在三件事:誰記住「上次抓到哪裡」、邊界條件誰處理、重跑會發生什麼。中間那一項最吃人——等號要不要含(> 還是 >=,差一筆或重一筆)、同一秒內寫進來的多筆資料(時間戳精度不夠就會漏),還有最陰險的一個:交易還沒提交,但時間戳欄位的值已經寫好了。你在那一刻撈,撈不到它;等它提交之後,它的時間戳已經小於你的水位線,於是再也撈不到了。
這方法也是我們將線上資料導入數據中台時,一開始想要做的作法,因為這方法複雜度低,但也才發現不是所有方法都能適用,技術的導入會受到原有資料架構的限制。
三、觸發器法:站在寫入的路徑上
在來源表掛一個觸發器(trigger),每次有人 INSERT、UPDATE、DELETE,就順手往一張「變更記錄表」寫一筆。
它把查詢法的三個缺口都補上了:刪除抓得到、每一次變更都抓得到、不需要依賴應用程式維護的時間欄位。
代價也很直接:
它在寫入的路徑上。 來源系統每寫一筆資料,資料庫要多做一次寫入。交易變長、鎖住資料的時間變長。平常感覺不出來,尖峰時段或批次作業的時候會。
它要改來源資料庫的結構。 加觸發器、加記錄表,都是對正式環境的結構變更。在受法規監理的產業裡,這件事的流程成本往往比技術成本高得多。
最嚴重的一點:觸發器出錯,觸發它的那個寫入會跟著失敗。 Informix 的文件講得很直接:觸發器失敗時,資料庫會把觸發敘述與觸發器做的事一起回滾(roll back)2——對使用者來說,那就是「存不進去」。你的資料同步需求,變成了來源系統的營運風險。對一套二十四小時不能停的核心系統來說,這一條通常就足以否決這個方案。
你可能會想:另外兩種做法出錯,不也一樣有影響嗎?
差別在站的位置。只有觸發器站在來源系統的寫入交易裡面。 查詢法站在讀取端,它出錯是查詢失敗或變慢,來源系統照常寫入;日誌法讀的是已經寫完的日誌,它出錯是同步落後,來源系統也照常寫入。
另外兩種最壞的情況是「同步壞了」,觸發器法最壞的情況是「來源系統壞了」。
為什麼要這樣設計:你在手工重建資料庫的日誌
那張變更記錄表不是隨便記記就好,它要記六件事,每一件都有非它不可的理由。拿 SQL Server 內建 CDC 的「變更表」來對照會很清楚——那是同一件事的官方版本:
| 變更記錄表要記什麼 | 為什麼非記不可 | SQL Server 的官方做法 |
|---|---|---|
| 操作類型 | 下游對新增、修改、刪除的處理完全不同 | __$operation:1 刪除、2 新增、3 更新前、4 更新後 |
| 先後順序 | 同一列被改了三次,套用順序錯了結果就錯 | __$start_lsn(交易編號)加上 __$seqval(同一交易內的排序) |
| 整列的值,不是只有變的欄位 | 下游可能還沒有這一列,只給變更欄位它拼不出完整的列 | 變更表的欄位鏡像來源表 |
| 更新前的舊值 | 下游要靠舊值才知道「改的是哪一列」 | 一次更新拆成兩列:更新前一列、更新後一列 |
| 哪些欄位變了 | 有些下游只關心特定欄位的變動 | __$update_mask 位元遮罩 |
| 已經消費到哪裡 | 下游讀到哪、哪些可以清掉 | 依編號範圍查詢,加上清理工作(預設保留三天) |
你用觸發器手工做的那張表,微軟用讀日誌的方式幫你做好了,而且欄位設計幾乎一樣3。差別只有一個:官方版站在日誌那一側,手工版站在寫入的路徑上。

維護成本也有具體的長相:來源端加一個欄位,你要改四個地方——記錄表加欄位,三個觸發器各改一次。漏改一個,那個欄位的變更就永遠傳不到下游,而且不會報錯。
四、日誌法:站在資料庫自己的日誌裡
資料庫本來就會把每一筆變更寫進交易日誌——Oracle 叫 redo log,Informix 叫 logical log,PostgreSQL 叫 WAL4。那份東西的原始用途是災難復原:機器掛了之後,資料庫靠它把沒寫完的交易重放一次。
日誌法的想法很簡單:既然資料庫已經幫我們記好了,直接去讀那份記錄就好。
它的好處是前兩種做法都給不了的:
- 對來源系統幾乎零侵入:不改表結構、不掛觸發器、不在寫入的路徑上。它讀的是資料庫已經寫好的檔案
- 刪除、每一次變更,全都抓得到:因為日誌本來就要記錄這些,否則災難復原會不完整
- 抓得到「改之前的值」:日誌裡不只有新的值,也有舊的值。這一點在下一篇會變成主角
代價有三個:
- 要碰資料庫的日誌層,需要 DBA 權限與額外設定,不是應用團隊自己就能開起來的
- 各家的日誌格式都不一樣,而且是內部格式。換一套資料庫,等於換一套工具
- 日誌有保存期限。管線停太久,需要的那段日誌已經被覆蓋掉,就只能重新做一次全量
五、三種做法的對照
| 查詢法 | 觸發器法 | 日誌法 | |
|---|---|---|---|
| 抓得到刪除嗎 | 抓不到 | 抓得到 | 抓得到 |
| 抓得到中間狀態嗎 | 抓不到 | 抓得到 | 抓得到 |
| 對來源系統的負擔 | 每次查詢都要掃描 | 每次寫入都多寫一筆 | 幾乎沒有 |
| 要不要改來源結構 | 要有時間欄位 | 要加觸發器與記錄表 | 不改結構,但要設定 |
| 延遲 | 看排程頻率 | 接近即時 | 接近即時 |
| 建起來有多難 | 低——寫個排程就能上線 | 中——要設計記錄表、寫三個觸發器,而且動的是正式環境 | 高——要 DBA 權限、要懂這套資料庫的日誌設定、要選一套工具 |
| 長期維護的負擔 | 中——邊界條件得自己一直顧(等號、同一秒多筆、未提交的交易) | 高——來源加一個欄位,記錄表與三個觸發器都要跟著改 | 低——多數雜事由工具處理,剩下日誌保存期與故障復原要顧 |
| 出錯時最嚴重會怎樣 | 漏抓,而且不會有人發現 | 來源系統的寫入跟著失敗 | 同步落後;日誌被覆蓋就得重做全量 |
| 誰在保證它的正確 | 應用程式的紀律 | 觸發器的邏輯 | 資料庫自己的日誌 |
| 本質上靠什麼 | 紀律 | 一半結構、一半紀律 | 結構 |
| 是不是資料庫原生功能 | 不是,用一般 SQL 自己拼 | 不是,用觸發器自己拼 | 是,各家都做成了正式功能 |
先看中間那兩列,它們的方向是相反的:最容易開始的那一種,長期成本最高。查詢法寫個排程就能上線,但那些邊界條件會一直回來找你;日誌法門檻最高——要權限、要懂日誌設定——可是建好之後,多數雜事是工具在做。
這張表的重點在下面那幾列。從上到下,正確性的保證從「人有沒有照規矩做」一路換成「資料庫本來就會做的事」;而最後一列說明了為什麼會這樣——只有日誌法需要資料庫原生支援,也只有它被各家做成了正式功能,另外兩種都是你用資料庫的一般能力自己拼出來的。
這也是我在其他題目上反覆遇到的同一件事:能從結構裡取得的保證,就不要靠紀律去維持。
順便把三個詞分開:目標、方法、工具
看完這三種做法,這裡順便把三個很容易混在一起的詞分開——很多討論之所以卡住,是因為把它們當成同一層的東西在比:
| 層次 | 是什麼 | 情境 |
|---|---|---|
| 目標 | 你要解決的問題 | 哪些資料變了,怎麼正確送到另一邊 |
| 方法 | 用什麼手法解決它 | 查詢法、觸發器法、日誌法 |
| 工具 | 誰來執行 | Airflow(排程器)、Debezium、資料庫內建的 CDC 功能 |
Airflow 那類排程工具不是 CDC 工具,它只負責「時間到了,執行這段程式」。真正在做 CDC 的,是你寫在裡面的那段 SQL。所以「用 Airflow 做增量抽取」與「用 Debezium 讀日誌」不是兩種工具之爭,是兩種方法之爭。
還有一個常見的誤會:CDC 不等於即時。即時是日誌法帶來的附加效果,不是 CDC 的定義。一天跑一次的查詢法,一樣是 CDC——它回答的是同一個問題:哪些資料變了。
六、為什麼主流收斂到日誌法——以及它不是唯一答案
看完三種做法的對照,日誌法勝出好像是理所當然的。但我想把理由講清楚,因為它不是「比較新」或「比較潮」。
前兩種做法的缺口是結構性的。 查詢法看不到刪除,不是因為實作不夠好,是因為被刪掉的資料真的不在那裡了;觸發器法會影響來源系統,不是因為程式寫得爛,是因為它本來就站在寫入的路徑上。這兩個缺口,換人做、換語言寫、多給預算,都補不起來。
日誌法之所以能同時避開這兩個問題,是因為它站在一個特別的位置:資料庫為了自己的可靠性,已經把所有變更都記下來了。 CDC 只是搭了這個便車。
不過我要誠實地講反面——這不代表每張表都該用日誌法。
一條 CDC 管線是一個要長期照顧的東西:日誌保存期要監控、來源端改結構要處理、故障之後要知道從哪裡接回去。這些維護成本是固定的,不會因為那張表只有五千筆資料就變少。
所以對那些資料量小、變更不頻繁、可以忍受一天一次延遲的表,每晚整批重抓一次,往往比維護一條 CDC 管線更划算——而且它天生沒有「接縫」問題,每次重抓都是完整的。
判斷的標準是這四個:表有多大、變更有多頻繁、能忍受多少延遲、有沒有人長期維護這條管線。前三個是技術問題,第四個才是決定性的。
七、開源套件選了哪一種,以及它用行為回答了我們的問題
現在最常被提到的開源 CDC 工具是 Debezium,它走的是日誌法。
但我想講的不是它選了哪一種,而是它在一個特定情況下的行為——那個行為,剛好直接回答了開場那個問題。
Debezium 的官方文件寫著:如果一張表沒有主鍵,連接器不會發出 UPDATE 和 DELETE 事件,只發出新增事件5。
不是效能比較差、不是需要額外設定,是直接不發。
你可以用 message.key.columns 這個設定,明確指定拿哪幾個欄位當作那張表的鍵,來繞過這個限制。但那個設定長這樣——表名:欄位,欄位,多張表用分號隔開5。表名可以用正則表達式一次涵蓋很多張表,但欄位名必須一個一個寫出來——也就是說,你得先知道每一張表該拿哪幾個欄位當鍵。而那,正是你原本想問工具的問題。
我第一次讀到這段的時候愣了一下,後來覺得這是我看過最誠實的設計決定。一個 UPDATE 事件的意思是「把目標端的某一列改成這樣」,一個 DELETE 事件的意思是「把目標端的某一列刪掉」。如果工具不知道是哪一列,它有兩個選擇:猜,或是不做。
Debezium 選了不做。
所以「CDC 開了,資料就會同步嗎」這個問題,業界最成熟的開源工具已經用行為回答過了:不會。 它甚至不會讓你走到出錯那一步,它會直接告訴你——這張表我不處理。
八、Oracle 與 Informix:兩條路,同一個問題
我接觸到的醫療核心系統,原有資料庫多半是Oracle 與 Informix這兩套的其中一套。它們對 CDC 的態度差很多,值得並排看一次。
在看這兩家之前,先把格局講清楚:三種做法是手法的分類,不是「每家資料庫提供三個選項」。各家做成正式功能的原生 CDC,一律是日誌法——這是查得到的共同格局:
| 資料庫 | 它的交易日誌叫什麼 | 原生的 CDC 怎麼做 |
|---|---|---|
| SQL Server | transaction log | 內建 CDC 功能,官方明講變更資料的來源就是交易日誌3 |
| Oracle | redo log | 原生 CDC 12c 起移除,官方指向 GoldenGate6;開源路徑走 LogMiner7 |
| PostgreSQL | WAL | logical decoding 把 SQL 造成的修改串流給外部消費者8 |
| MySQL | binary log | binlog 記錄描述資料庫變更的事件,用於複製與還原9 |
| MariaDB | binary log | 同樣是 binlog;Debezium 為它另備一個獨立的連接器10 |
| Informix | logical log | CDC API 讀 logical log11 |
反過來說,查詢法與觸發器法,沒有任何一家把它們做成「CDC 功能」。因為它們不需要資料庫提供什麼特別能力——一個時間欄位、一個觸發器,都是你自己就能做的事。所以更準確的講法是:只有日誌法需要資料庫原生支援,也只有它被各家做成了正式功能;另外兩種,是你用資料庫的一般能力自己拼出來的。
還有一個跨廠商的細節值得記著:SQL Server 把一次更新拆成「更新前」與「更新後」兩列3,跟 Informix 的兩種更新記錄型別12 完全一樣。不是誰抄誰,是日誌法都得解決同一個問題——下游必須知道「改之前長什麼樣」,才知道該改哪一列。

| Oracle | Informix | |
|---|---|---|
| 原生 CDC 功能 | 12c 起不再包含,官方明講已由 GoldenGate 取代6 | 內建 Change Data Capture API,讀 logical log11 |
| 要另外付費嗎 | GoldenGate 是獨立授權的商用產品6 | CDC API 是資料庫內建的功能 |
| 開源路徑 | Debezium 的 Oracle 連接器(預設走 LogMiner,也支援 XStream 與 OpenLogReplicator)7 | 沒有主流的開源連接器,多半自行開發或用第三方工具 |
| 前置設定叫什麼 | supplemental logging(補充記錄)13 | cdc_set_fullrowlogging()(全列記錄)14 |
| 改之前的值怎麼來 | 由 supplemental logging 的層級決定13 | 記錄型別直接分成「更新前」與「更新後」兩種12 |
| 初始全量誰做 | 工具通常自帶 | 文件明講不提供,要自己做11 |
這張表裡我覺得最有意思的一格,是沒有主鍵的時候,資料庫自己會怎麼辦。
Oracle 的文件把它的判斷順序寫得很清楚13:
- 這張表有主鍵 → 就記主鍵欄位
- 沒有主鍵,但有一個以上不可為空的唯一索引 → 挑其中一個唯一索引當作識別
- 兩個都沒有 → 除了 LONG 和 LOB 這類大欄位以外,整列的所有欄位都記下來
第三段的意思值得停下來想一下:當 Oracle 找不到任何一把可靠的鑰匙時,它的選擇是把整列都記下來,用「所有欄位的值都一樣」來確認這是同一列。
這個選擇很貴——日誌量會膨脹,下游比對的時候沒有索引可用。但它換到一件事:只要值對,就一定是對的那一列。
還有一個細節:第二段那個「挑其中一個」,文件沒有說是怎麼挑的。有多個唯一索引的時候,選擇權在資料庫手上,不在你手上。我找不到任何公開文件說明這個規則。
這件事本身就是一個結論:沒有寫在文件裡的行為,不能當成設計的依據。 它今天可能挑索引 A,某次升版或索引重建之後可能挑 B,而你不會收到通知——你的下游程式如果假設了某一把鑰匙,那個假設隨時可能在你不知情的狀況下失效。
也因為這樣,Oracle 官方直接建議:使用這個功能時,大部分的表都應該要有主鍵或唯一索引鍵13。
我想強調的是最後這一點。這不是某個顧問的意見,也不是教科書的理想狀況——是資料庫廠商在自己的文件裡,為了自己功能的正確性,請你把鍵補上。
九、CDC 不負責的三件事
日誌法解決了「怎麼把變更讀出來」。但有三件事,它從一開始就沒有承諾要做。

第一件:既有的那幾千萬筆資料
Informix 的 CDC API 文件講得很直接:它只捕捉開始擷取之後發生的變更,不提供表內現有內容的快照11。
意思是,完整的串接一定是兩段:先做一次全量複製,再從某個日誌位置接上增量。
而這兩段之間有一個接縫,那個接縫是很多人踩過的坑:
- 接縫漏了——全量拍完到開始監聽之間發生的變更沒被抓到。結果是目標端永遠少那幾筆,而且不會有任何警訊,因為之後的同步一切正常
- 接縫重了——同一批變更被套用了兩次。這時候有沒有主鍵,結果天差地遠:有主鍵的話重複套用是安全的(同樣的更新做兩次,結果一樣);沒有主鍵的話,重複套用就是實實在在多出好幾列資料
第二點值得記下來,因為它把「要不要補主鍵」的理由從「怕改錯列」擴大到另一個層次:一條不能安全重跑的管線,每一次故障復原都得靠人工判斷該從哪裡接回去。 而故障一定會發生。
第二件:開始之後的結構變更
同一份文件也寫了:開始擷取之後的 schema 變更不會被捕捉11。
來源端加了一個欄位、改了一個欄位的長度,管線不會自己知道。
這其實不是技術問題,是責任歸屬的問題:來源系統改結構的時候,下游要怎麼事前知道?
順帶一提,這件事各家處理的程度不同。SQL Server 的 CDC 有一套明確的策略:啟用 CDC 不會阻止你對來源表做結構變更,但擷取程序會忽略那些「啟用當時沒被指定要擷取」的新欄位;被刪掉的欄位,之後的變更記錄補上 NULL;既有欄位改型別則會傳播到變更表。所有 DDL 事件另外記進一張歷史表,下游可以查3。
換句話說,不是不能加欄位,是加了之後 CDC 不會自動跟上——要讓新欄位進入同步,得為那張表另外建一個擷取設定(同一張表最多兩個,可以新舊並行一段時間再切換)3。代價是你仍然要有人去看、去決定什麼時候切。
Informix 的規則更嚴格,而且這一點是我查證之後才發現的:它的文件寫著,擷取進行中的表,你去 ALTER 它會直接失敗11。也就是說結構要改,得先停止擷取。好消息是停掉之後重新啟動,可以從當初停下來的那個日誌位置接續11。
同一個問題,兩家的處理方式剛好相反:SQL Server 讓你改、但同步不會跟上;Informix 不讓你改、逼你先停下來。後者其實比較不容易悄悄出錯——它會擋住你,而不是默默地漏掉那個欄位。不過這個嚴格在某些場合會變成難題:一套二十四小時不能停的系統,要先停止擷取才能改結構,而停機窗口正是這類系統最難排的東西。Informix 原生 CDC API 的這條規則,對它們來說是實際的限制。
理想的答案是上游宣告——改表之前先通報、把欄位清單當成一份雙方都認可的約定,改變之前要先協商。我同意這是對的方向,也必須誠實地說:當來源是一套買來的商用套裝系統時,下游通常沒有立場要求對方宣告任何事。
所以務實的做法是自己補一層:把來源的欄位清單納入版本控制,定期比對一次,有差異就跳出來。這一層靠的是紀律不是結構,但它至少把「悄悄地錯」變成「定期會有人發現」。
我們自己的串接又往前走了一步:來源端的結構變化會被自動偵測,管線也跟著調整,不必等人去比對。做到這一步,這件事就從紀律變回了結構——上面那一句「靠紀律」講的是最低標準,不是天花板。
第三件:這筆變更該套到哪一列
這就是開場那個問題,也是下一篇的全部內容。
這裡先講一件容易被跳過的事:CDC 交到你手上的,是來源表的原樣。
表名還是來源系統的表名,欄位名還是那些看不懂的縮寫,一個實體的資料散在七八張表裡。它是一份忠實的複本——但複本不等於可以用。業務單位打開它,一個欄位都認不得。
同步完成,跟資料可以用,中間還隔著整個建模的工作。 CDC 把水送到了,但廚房還沒開。
十、CDC 是水龍頭,不是廚房
回到開場那個問題。
CDC 啟用了,代表的是:來源系統裡每一筆變更,都會被讀出來,一筆不漏。這件事它做得非常好,而且對來源系統幾乎沒有負擔。
它不代表的是:那些變更會正確地落到目標端該落的位置。
水龍頭打開了,水會流出來——這是 CDC 的承諾。水會流進哪個桶子、會不會倒進別人的桶子裡,它從來沒有承諾過。決定這件事的,是那張表有沒有一把穩定的鑰匙。
而 Oracle 在文件裡請你補上主鍵、Debezium 在沒有主鍵時乾脆不發事件——這兩件事其實在講同一句話,只是一個用建議的,一個用行動的。
最後說幾件這篇沒談、但你遲早會遇到的事:
- 延遲到底多少:文中只說「接近即時」。實際量級取決於工具、設定與來源端的交易量,從秒到分鐘都有可能,這篇不做數字宣稱。
- 管線壞了怎麼發現:CDC 本身也是一套要被監控的系統。它停掉的時候目標端不會報錯,只會停止變新——這種「安靜的故障」需要另外設監控。
- 第二套來源系統接進來怎麼辦:兩套系統對同一個實體用不同的鍵、事件順序也各走各的,那是整合層要解的題,不是 CDC 能解的。
- 非關聯式資料庫:這篇談的是關聯式資料庫。MongoDB 的 change streams 是讓應用程式直接訂閱資料變更,底層讀的是它自己的操作日誌 oplog15;原理相通(都是讀那份為了可靠性而本來就存在的記錄),但名詞、保證與限制都得另外談。
下一篇,我們來看那三種鑰匙:主鍵、唯一鍵、還有那個看起來能用、其實不能用的 rowid。它們在「同一筆變更該套到哪一列」這個問題上,差別到底在哪裡;以及一張沒有主鍵的舊表,到底值不值得花力氣把主鍵補上。
Oracle Database Concepts(online redo log 記錄資料檔的變更)https://docs.oracle.com/en/database/oracle/oracle-database/19/cncpt/oracle-database-instance.html;
HCL Informix,Transaction logging(交易的記錄稱 log records,存放的空間稱 logical log)https://help.hcl-software.com/hclinformix/15.0.0/sqt/ids_sqt_279.html;
PostgreSQL,Write-Ahead Logging https://www.postgresql.org/docs/current/wal-intro.html (查證日 2026-09-20)
參考資料
- IBM,What Is Change Data Capture?:把 log-based、timestamp-based 與 trigger-based 列為最常見的幾種做法,並說明 timestamp-based 需要 schema 具備時間戳欄位、且在時間戳隨整列被刪除時捕捉不到刪除操作。https://www.ibm.com/think/topics/change-data-capture (查證日 2026-09-20) ↩
- HCL Informix 文件,BEFORE 與 AFTER 觸發動作:在有交易記錄的資料庫中,觸發器失敗時,資料庫伺服器會回滾觸發敘述與觸發動作所做的變更。https://help.hcl-software.com/hclinformix/1410/sqt/ids_sqt_530.html (查證日 2026-09-20) ↩
- Microsoft 官方文件,SQL Server 的 change data capture:變更資料的來源是 SQL Server 的交易日誌;擷取程序讀取日誌,把變更寫進變更表。同一頁也說明啟用 CDC 不會阻止來源表做結構變更,以及新增欄位、刪除欄位、型別變更各自的處理方式。https://learn.microsoft.com/en-us/sql/relational-databases/track-changes/about-change-data-capture-sql-server (查證日 2026-09-20) ↩
- 三家官方文件對交易日誌的定名與定義: ↩
- Debezium 官方文件,PostgreSQL 連接器:表沒有主鍵時不發出 UPDATE/DELETE 事件,只發出新增事件;可用
message.key.columns覆寫表的主鍵。https://debezium.io/documentation/reference/stable/connectors/postgresql.html 該設定的格式為「完整表名:欄位,欄位」、多張表以分號分隔,見 Confluent 的 Debezium 連接器設定文件:https://docs.confluent.io/kafka-connectors/debezium-mysql-source/current/mysql_source_connector_config.html (查證日 2026-09-19) ↩ - Oracle Database 12c 廢止功能文件,8.2.2 節:Oracle Change Data Capture 不包含在 12c 中,已由 Oracle GoldenGate 取代。https://docs.oracle.com/database/121/UPGRD/deprecated.htm (查證日 2026-09-19) ↩
- Debezium 官方文件,Oracle 連接器:可透過原生 LogMiner(預設)、XStream API 或 OpenLogReplicator 三種方式擷取變更。https://debezium.io/documentation/reference/stable/connectors/oracle.html (查證日 2026-09-20) ↩
- PostgreSQL 官方文件,Logical Decoding:把經由 SQL 造成的修改串流給外部消費者;UPDATE 與 DELETE 的舊值是否可得,取決於該表的 REPLICA IDENTITY 設定。https://www.postgresql.org/docs/current/logicaldecoding.html (查證日 2026-09-20) ↩
- MySQL 官方手冊,The Binary Log:binlog 包含描述資料庫變更的事件,用於複製與資料還原。https://dev.mysql.com/doc/refman/8.4/en/binary-log.html (查證日 2026-09-20) ↩
- MariaDB 官方文件,Binary Log:binlog 記錄資料庫的所有變更,格式分為 statement、row 與 mixed 三種。Debezium 另備獨立的 MariaDB 連接器,讀 binlog 產生列層級的變更事件:https://mariadb.com/kb/en/binary-log/ 、https://debezium.io/documentation/reference/stable/connectors/mariadb.html (查證日 2026-09-20) ↩
- HCL Informix 文件,Change Data Capture API:讀 logical log 並依序處理交易;不提供表內容的初始快照;資料庫必須啟用 logging;不捕捉表結構的變更或其他資料庫變更;擷取進行中對該表執行 alter 會失敗;停止擷取後重新啟動,可從停止時的 logical log 位置接續。https://help.hcl-software.com/hclinformix/1410/cdc/ids_cdc_057.html (查證日 2026-09-19,ALTER 與重啟兩項 2026-09-20 補查) ↩
- HCL Informix 文件,CDC 記錄格式:記錄型別包含 CDC_REC_UPDBEF(更新前的整列影像)與 CDC_REC_UPDAFT(更新後)。https://help.hcl-software.com/hclinformix/1410/cdc/ids_cdc_029.html、https://help.hcl-software.com/onedb/2.0.1/cdc/ids_cdc_040.html (查證日 2026-09-19) ↩
- Oracle 文件,Database-Level Supplemental Logging:無主鍵但有不可為空的唯一索引時,挑其中一個作為識別;兩者皆無時,除 LONG 與 LOB 外所有欄位都會被補充記錄;官方建議大部分表都應具備主鍵或唯一索引鍵。文件未說明多個唯一索引時的挑選規則。https://docs.oracle.com/database/121/SUTIL/GUID-D2DDD67C-E1CC-45A6-A2A7-198E4C142FA3.htm (查證日 2026-09-19) ↩
- HCL Informix 文件,
cdc_set_fullrowlogging():對要擷取的表啟用全列記錄,需以 informix 使用者執行。https://help.hcl-software.com/hclinformix/1410/cdc/ids_cdc_059.html (查證日 2026-09-19) ↩ - MongoDB 官方文件,Change Streams:讓應用程式取得即時的資料變更,不必自行 tail oplog(複本集的操作日誌)。https://www.mongodb.com/docs/manual/changeStreams/ (查證日 2026-09-20) ↩
延伸閱讀
如果你也走在自己的路上,歡迎訂閱《小穗步電子報》——我會把每一次的覺察與練習,寫成信寄給你。→ 訂閱小穗步電子報