具代表性的面試主題

資料工程面試:Iceberg v3 的 row lineage 如何保證穩定行識別?

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

題幹

你要在 Iceberg v3 表上做增量更新和跨快照稽核,如何使用 row lineage 維護 _row_id 與更新時間,並處理並發提交、重試和刪除語意?

題幹與適用場景

一張 Iceberg v3 事件表需要支援 CDC、重算和跨快照稽核。團隊希望每行有可追蹤的穩定識別,並能知道最後更新屬於哪次提交。請解釋 row lineage 的欄位、繼承時機、快照提交重試、equality delete 限制和驗證方案。

面試官考察點

  • 是否理解 _row_id_last_updated_sequence_number 的繼承,而非寫入時臆造。
  • 是否知道 first-row-idnext-row-id 和 manifest 的關係。
  • 是否能處理 optimistic commit 重試、並發寫入和舊讀取器相容。
  • 是否區分 row lineage、業務主鍵、equality delete 與實體檔案清理。

回答前需要釐清的問題

  1. 要追蹤的是物理行身份、業務實體身份,還是兩者都要?
  2. 讀取器是否全部支援 Iceberg v3,舊引擎如何降級?
  3. 更新採用 copy-on-write、merge-on-read 還是 equality delete?
  4. 稽核要求跨快照重放,還是只需目前表的最新版本?

30 秒回答框架

Iceberg v3 的 row lineage 用 _row_id 標識表內行,用 _last_updated_sequence_number 表示最後更新提交;值在讀取時透過 data file 的 first-row-id、行位置和 manifest sequence number 繼承。寫入階段先生成帶 null 的檔案,提交成功後由 metadata 確定繼承值,因此提交重試必須重新分配 first-row-id。它不等於業務主鍵,也不能讓 equality delete 保留原行 ID。先建立 v2/v3 讀取能力矩陣,再用快照、並發衝突和重試測試驗證。

分步驟深入解答

1. 區分兩種身份

業務主鍵回答「這是什麼實體」;_row_id 回答「這張表中的哪一行」。同一實體被刪除後重新寫入,業務鍵可以相同,但 row lineage 不應被誤當成永久實體 ID。稽核系統應同時保留業務鍵、快照 ID 和 row ID。

2. 理解欄位繼承

新行的 _row_id_last_updated_sequence_number 可以在 data file 先寫成 null。讀取時,row ID 由檔案的 first-row-id 加行位置推導,更新時間由 manifest entry 的 sequence number 推導。寫入器不必在提交成功前重寫資料檔。

text
row_id = data_file.first_row_id + row_position
last_updated = manifest_entry.data_sequence_number

3. 處理提交重試

樂觀並發提交失敗後,表的 next-row-id 可能已變化。重試必須重新讀取目前 metadata,重新分配 first-row-id,並重新生成 manifest list;不能重用第一次嘗試的行號範圍。提交日誌記錄 attempt、衝突原因和最終 snapshot ID。

4. equality delete 的邊界

使用 equality delete 的引擎通常不讀取舊資料行就寫入變更,因此無法提供被替換舊行的原始 row ID。規範把這類更新視為舊行被移除、再新增唯一行。需要穩定業務身份時,另行維護業務鍵和變更事件,不能假設 equality delete 自動延續 row lineage。

5. 相容與讀取

v3 引入 row lineage,但舊讀取器可能不了解保留欄位。升級前建立引擎能力矩陣,確認舊讀者看到 null、忽略欄位或直接失敗的行為。對外匯出不要依賴讀者自動推導 row ID,必要時由支援 v3 的服務先物化稽核欄位。

6. 驗證、回滾與清理

測試單次寫入、並發提交、衝突重試、快照回讀、刪除後重寫和 compaction。驗證同一 snapshot 內 row ID 唯一、重試不會重用舊範圍、sequence number 與最終 snapshot 對齊。回滾只切換快照引用,不重寫歷史 row ID;實體檔案清理由快照過期和 orphan cleanup 負責。

高品質示範回答

我會把業務主鍵和 Iceberg row lineage 分開。v3 用 _row_id_last_updated_sequence_number 提供表內行身份和提交順序,它們可由 data file 的 first-row-id、行位置和 manifest sequence number 在讀取時繼承。寫入先保留 null,提交衝突重試時重新讀取 metadata 並重新分配 ID,不能重用舊 manifest。equality delete 不保證延續舊 row ID,所以稽核要保存業務鍵、snapshot ID 和事件。上線前驗證 v2/v3 讀取、並發衝突、快照回讀、compaction 與清理,並把回滾限定為快照引用切換。

常見錯誤

  • 把 row ID 當業務主鍵 → 刪除重建後語意錯誤 → 同時保存業務鍵和 snapshot。
  • 在寫檔時永久分配 row ID → 重試可能重複或浪費範圍 → 依賴提交階段的繼承。
  • 重用衝突提交的 manifest → 使用過期 next-row-id → 每次重試重新讀取 metadata。
  • 認為 equality delete 會保留原 row ID → 規範不保證舊行可讀 → 用業務事件追蹤實體。
  • 只驗證目前查詢 → 快照和舊讀取器仍可能不相容 → 覆蓋跨快照和能力矩陣。

追問及應對

為什麼不用業務主鍵代替 _row_id

業務鍵可能重複、變更或跨表不唯一;row lineage 是表內物理行身份。兩者服務不同稽核問題,應同時記錄。

提交衝突重試會不會讓 row ID 出現空洞?

可能出現未被最終快照使用的範圍,具體回收策略由實作決定。重點是不能重用已暴露給成功快照的 ID,且最終快照中的 ID 保持唯一。

compaction 會改變 row ID 嗎?

正確實作應透過 lineage 繼承保留行身份;驗證 compaction 前後同一快照語意和稽核映射,不能只比較檔案路徑。

公開來源

同類題目