具代表性的面試主題

資料工程面試:如何設計從數倉到業務系統的 Reverse ETL 同步?

資料困難
Offer.cc 編輯團隊發佈 更新

題幹

數倉中的客戶模型每 15 分鐘更新一次,需要同步到 CRM 與行銷系統。請設計一條支援 3,000 個租戶的 Reverse ETL 鏈路:高優先級人群的新鮮度 SLO 為 10 分鐘,目的端有每租戶限流,來源資料至少一次交付,並且必須處理 schema 漂移、重試、刪除和同意撤回。

題幹與適用場景

這是資料平台與資料工程系統設計題。Reverse ETL 把數倉中的可信模型投遞到 CRM、行銷或產品工具;Hightouch 官方說明將流程概括為 source → model → sync → destination,Census 資料也把數倉到業務平台的同步定義為 operational analytics。題目要求你同時處理批次、增量變更、目的端 API 約束和資料治理。

面試官考察點

  • 能否把模型快照、變更偵測、排程、佇列和目的端 adapter 拆開。
  • 能否在來源至少一次交付下,用冪等鍵和版本保護目的端。
  • 能否把刪除、同意撤回、schema 漂移和租戶隔離設計成明確契約。
  • 能否用新鮮度、成功率、積壓和對帳指標證明系統可用,而非只畫資料流圖。

回答前需要釐清的問題

  • 10 分鐘 SLO 只約束高優先級人群,還是所有記錄?
  • 目的端是否支援批次 upsert、刪除介面、冪等鍵和服務端游標?
  • 模型是否有穩定主鍵、更新時間和刪除墓碑?快照保留多久?
  • 租戶之間的配額是否獨立,單一大租戶能否占滿全域吞吐?
  • 同意撤回需要多快在目的端生效,失敗期間是否必須阻止新增同步?

「我把系統拆成版本化模型、變更偵測器、每租戶佇列、目的端 adapter 和對帳任務。每筆記錄帶 tenantid、穩定業務主鍵、模型版本、rowversion 和刪除狀態。偵測器用高水位或 CDC 產生至少一次任務,adapter 按目的端限流批次 upsert,並以 tenant、destination、recordid、rowversion 組成冪等鍵。重試不會降低版本;刪除和同意撤回先寫不可繞過的 fence。監控新鮮度 lag、積壓、限流、失敗分類、對帳差異和目的端刪除延遲。」

分步驟深入解答

第一步:定義來源模型與版本

把數倉模型作為同步輸入,不讓 worker 直接拼接多個業務庫。模型輸出穩定的 record_idtenant_id、業務欄位、row_versionupdated_atconsent_statedeleted_at。每次模型執行產生 model_run_id;記錄一旦被刪除,先輸出墓碑而不是從查詢結果中靜默消失。這樣可以區分「本輪尚未掃描」與「明確要求目的端刪除」。

第二步:偵測變更與排程

優先使用模型表的更新欄位或 CDC 水位;水位應保存在持久化 checkpoint 中,並允許重疊視窗,避免相同時鐘導致漏數。一次執行寫入不可變更批次,再由排程器按租戶優先級拆成任務。高優先級佇列按 10 分鐘 SLO 計算允許延遲,低優先級任務在全域容量不足時讓路,但不能繞過同意撤回佇列。

第三步:設計冪等 upsert

任務至少一次投遞,因此「傳送成功後 worker 崩潰」必須安全重播。對目的端支援冪等鍵的介面,使用 tenant_id + destination + record_id + row_version;目的端只接受不低於目前版本的寫入。若目的端沒有冪等能力,保存請求指紋和回應、限制並行,並用週期性讀取對帳;不要聲稱跨系統交易能提供 exactly-once。重試必須按可重試錯誤、指數退避和最大嘗試次數分類。

第四步:隔離限流與過載

每租戶維護令牌桶或目的端返回的剩餘配額,同時設定全域並行上限。租戶佇列、公平排程和死信佇列避免單一大租戶拖垮其他租戶。429、5xx 和網路逾時進入延遲重試;4xx schema 或權限錯誤進入人工處理佇列。佇列積壓接近 SLO 時觸發告警,並允許暫停低優先級全量回填。

第五步:處理刪除與同意撤回

撤回事件寫入獨立 deletion fence,帶 tenant、record_id 和事件版本。worker 傳送 upsert 前檢查 fence;已撤回記錄只允許傳送刪除,直到治理策略確認解除。目的端刪除成功後保留回執和時間戳,失敗則持續重試並告警。定期對帳應檢查目的端仍存在的禁止記錄,不能只統計請求成功率。

第六步:應對 schema 漂移與回滾

模型 schema 以版本發布,欄位映射在部署前做相容性檢查。新增可選欄位可灰度,型別變更或刪除欄位先產生阻斷報告,不直接讓所有租戶失敗。adapter 保留 mapping_version,失敗批次綁定舊映射重試;需要回滾時切回已驗證版本,禁止把半遷移狀態覆蓋成最新成功。

第七步:可觀測性與對帳

按租戶和目的端記錄 source_run、任務狀態、嘗試次數、最後成功版本、API 延遲、限流次數、佇列年齡和刪除延遲。核心指標包括高優先級記錄的 freshness lag p95、成功率、死信數、schema 錯誤率、目的端與來源端計數差異,以及隨機抽樣的欄位 hash 差異。每日或每次發布後跑全量對帳,自動修復可安全重播的差異,並把不可修復項交給營運。

設計取捨與邊界

快照、增量與 CDC

純快照簡單但會重複掃描;更新時間增量成本低,卻依賴穩定時鐘和更新欄位;CDC 能表達刪除,但要求來源表或建模層保留變更事實。面試中應說明選擇取決於模型刷新方式、刪除語義和目的端容量,並保留定期全量對帳作為漏數保險。

佇列位置與一致性

按租戶分區便於隔離和順序保證;全域佇列更容易利用容量,但需要公平排程。可以保證同一記錄版本單調可見,卻無法在數倉提交和目的端寫入之間提供跨系統原子提交。用版本條件寫入、重播和對帳換取可解釋的最終一致性。

全量回填與即時更新

回填應使用獨立低優先級預算、可暫停游標和限流感知;即時更新進入高優先級佇列。回填與即時任務競爭同一記錄時,以更高 row_version 勝出,並在目的端支援條件寫入時拒絕舊版本。

高品質示範回答

「我會把數倉模型當成版本化事實源,先由高水位或 CDC 生成不可變更批次,再按租戶和目的端排隊。記錄帶穩定主鍵、row_version、模型和映射版本;upsert 使用目的端冪等鍵或請求指紋,重試採用指數退避,舊版本不能覆蓋新版本。每租戶令牌桶和全域並行上限共同處理限流。刪除與同意撤回寫入 fence,worker 傳送前強制檢查,目的端保留刪除回執。透過 freshness lag、佇列年齡、死信、schema 錯誤、欄位 hash 對帳和禁止記錄殘留來驗收,明確系統提供至少一次投遞與最終一致性,而非跨系統 exactly-once。」

常見錯誤

  • 把 Reverse ETL 說成即時資料庫複製,忽略模型刷新和業務欄位映射。
  • 只說「訊息佇列保證 exactly-once」,沒有處理目的端重試和重複請求。
  • 用全域限流代替租戶隔離,導致一個大租戶擠占全部容量。
  • 從目前模型查詢中消失就當作刪除,沒有墓碑、同意撤回 fence 和目的端對帳。
  • schema 變更直接廣播,失敗後無法知道哪一批使用了哪一版映射。

延伸追問

如何證明 10 分鐘新鮮度 SLO

從模型提交時間或變更事件時間開始,到目的端確認可讀結束,按高優先級記錄計算 p95 與逾時率。不要用 worker 啟動時間替代資料產生時間,也不要只看平均值。

目的端只有覆蓋式全量介面怎麼辦

為每租戶生成帶 model_run_id 的版本化快照,先上傳臨時集合並校驗計數與 hash,再原子切換版本;刪除和撤回仍需單獨 fence,不能依靠下一次全量自然消失。

如何處理目的端成功但回執遺失

重播同一冪等請求,或依據請求指紋和目的端查詢做對帳。若介面無法查詢且沒有冪等鍵,只能把不確定結果放入人工核對佇列,不能無證據地標記成功。

什麼時候需要專門的同步平台

當目的端數量、租戶配額、映射版本、治理圍欄和對帳要求超過單一 DAG 的可維護範圍時,再拆成持久化任務服務和 adapter 層。小規模單目的端可用編排器加冪等腳本起步,但仍應保留刪除與重試契約。

公開來源

同類題目