資料工程面試:如何為事件 Schema 設計相容性發布閘門?
題幹與適用場景
訂單事件由多個團隊生產與消費。新版本要增加可選欄位、重命名一個欄位並淘汰舊欄位;消費者無法同時升級,歷史訊息仍會重放。請設計從變更提案、相容性檢查、灰度發布到回滾的閘門,並說明 Avro writer/reader schema、Schema Registry 相容模式和資料品質驗證如何配合。
這題考察資料契約、Schema 演進、串流發布與營運治理。Schema Registry 能集中儲存版本、校驗相容性並讓生產者與消費者共享契約,但具體規則會隨 Avro、JSON Schema 或 Protobuf 格式而變。
面試官在考察什麼
- 是否區分 backward、forward、full 與 transitive,而不是把「相容」當成單一布林值。
- 能否從 writer schema 與 reader schema 的實際部署窗口推導發布順序。
- 能否把註冊前檢查、執行期 schema ID、消費者 lag 和資料品質接成閘門。
- 能否處理重命名、預設值、刪除、列舉和不可逆變更的遷移與回滾。
先釐清哪些問題
- 使用 Avro、JSON Schema 還是 Protobuf?格式不同,解析與相容細節不同。
- 消費者是即時、批次還是會重放歷史?最老 writer 版本保留多久?
- 生產者和消費者誰先發布,是否存在跨區域或跨團隊的長尾版本?
- Topic 按團隊、事件類型還是環境劃分 subject?相容級別是全域還是按 subject?
- 回滾是恢復舊生產者、停止發布,還是需要雙寫新舊欄位?
30 秒回答框架
先建立版本與消費者窗口,再選相容方向:新消費者讀舊訊息需要 backward,舊消費者讀新訊息需要 forward,兩者都要求時才考慮 full,並依最老版本選擇 transitive 檢查。所有 Schema 先在註冊閘門校驗,再按「讀者先、寫者後」安全順序灰度;新增欄位提供預設值,重命名採 alias 或雙欄位遷移,刪除要等消費者窗口結束。執行期監控 schema 註冊拒絕、反序列化錯誤、lag、死信與關鍵欄位品質,異常時停止推廣並保留舊版本。
分步作答
1. 明確 writer 與 reader 的部署矩陣
訊息攜帶或關聯 writer schema;消費者用 reader schema 解析。畫出舊生產者/新生產者與舊消費者/新消費者四種組合,標記哪些組合必須支援。即時升級通常先讓新消費者能讀舊資料,再讓舊消費者能讀新資料,最後才切換生產者。
2. 把相容模式綁定到風險
Backward 檢查新 reader 能否讀舊 writer;forward 檢查舊 reader 能否讀新 writer;full 同時檢查兩者。若需要跨越多個歷史版本,選 transitive 語意或顯式測試版本集合。不要把某個註冊中心預設值當成所有格式的通用規則。
3. 設計變更規則
增加欄位時給出安全預設值並驗證業務含義;重命名優先使用格式支援的 alias,或先雙寫舊、新欄位,再遷移 reader;刪除欄位要等最老消費者與重放窗口結束。列舉新增值要確認舊消費者的未知值行為,型別收窄、含義改變與單位改變應視為破壞性變更。
propose -> lint -> compatibility-check -> consumer-matrix-test
-> register -> canary-producer -> observe -> expand4. 建立註冊與 CI/CD 閘門
PR 階段執行格式 lint、規範化 diff、subject 級相容檢查與代表性歷史樣本反序列化。註冊中心保留 schema ID 與版本;發布工具拒絕繞過檢查的生產註冊。相容級別可按 subject 管理,不能只改全域設定而忘記局部覆蓋與權限稽核。
5. 按讀者先、寫者後的順序灰度
先發布能讀舊版本的新消費者,觀察反序列化錯誤與 lag;再雙寫或發布新生產者;最後停止舊消費者和舊欄位。跨團隊長尾消費者要有清單、負責人與截止時間,不能用「大家升級後再發」作為控制面。
6. 執行期品質與回滾
監控 schema ID 未知、反序列化失敗、死信量、欄位缺失率、單位異常、消費者 lag、重放成功率與註冊拒絕數。回滾優先停止新生產者並恢復仍相容的舊 writer;若語意已改變,保留新 topic 或版本化 subject,避免把舊資料強行解釋成新含義。
高品質示範答案
我會先畫出 writer/reader 部署矩陣,確認要支援的舊生產者、舊消費者和重放版本,再按格式選擇相容規則。新 reader 讀舊訊息是 backward,舊 reader 讀新訊息是 forward,兩者都要支援才考慮 full;跨多個歷史版本則做 transitive 檢查。所有變更先經過 lint、subject 級註冊檢查與歷史樣本反序列化。
發布順序是讀者先、寫者後:先灰度新消費者,再雙寫或切換新生產者,最後等待消費者窗口結束後刪除舊欄位。新增欄位給預設值,重命名用 alias 或雙欄位過渡,刪除和列舉變化都要驗證舊客戶端行為。線上觀察註冊拒絕、反序列化錯誤、lag、死信與欄位品質;異常時停止擴散、保留舊 schema,必要時回到舊生產者或新舊 topic 分流。相容模式必須按具體格式驗證,不能把一個產品的預設規則當成 Avro、JSON Schema、Protobuf 的共同語意。
常見失分點
- 只說「啟用 backward」,卻不說明誰是 reader、誰是 writer。
- 直接重命名或刪除欄位,忽略 alias、雙寫和歷史重放。
- 只在註冊時檢查 Schema,不測試真實歷史訊息和舊消費者。
- 先發布新生產者,導致舊消費者收到無法解析或語意錯誤的資料。
- 把全域相容設定當成 subject 級策略,忽略權限和設定漂移。
- 只監控 Kafka lag,不監控欄位缺失、單位變化、死信與反序列化錯誤。
追問與參考回答
新增欄位為什麼常要求預設值?
舊訊息沒有該欄位,新 reader 需要確定值才能建立記錄;預設值必須符合業務語意,不能用空值掩蓋必填資料缺失。
重命名欄位應直接改名嗎?
通常不應直接改。可先用 alias 或同時寫舊、新欄位,遷移所有 reader 後再刪除舊欄位,並保留足夠重放窗口。
full 與 full transitive 有什麼風險差異?
full 通常檢查目前相鄰版本的雙向相容;transitive 會擴大到歷史版本集合,閘門更嚴格但升級成本更高,應依保留與重放範圍選擇。
為什麼 schema 註冊成功仍可能遺失資料?
相容性只覆蓋結構解析,不保證業務單位、列舉語意、欄位品質、權限或下游 SQL 正確;要結合樣本回放、品質規則和執行期觀測。
如何為長尾消費者設定退出條件?
登記 owner、版本、最後消費時間與重放需求,設定明確截止日期與告警;到期前提供遷移工具,到期後先隔離或拒絕舊版本,而不是永久放寬相容規則。
什麼時候應該新建 subject 或 topic?
當語意、單位、生命週期或權限邊界已改變,無法透過相容演進表達時。新版本隔離可降低誤解析風險,但要承擔雙寫、回放和治理成本。