題目與適用情境
一張 Apache Iceberg 事件資料表約有 1 PiB,每天新增 6 TiB,保留 180 天。所有查詢都帶有 eventtime 範圍,通常查詢 1 到 7 天;其中 35% 還會等值篩選 12,000 個 tenantid 之一。事件最多延遲 48 小時,也可能執行歷史回填。目前未分割的資料表掃描過多資料。
請選擇初始分割區規則,說明常見替代方案為何失敗,並解釋如何證明裁剪生效。回答需要涵蓋述詞寫法、資料傾斜、檔案大小、延遲資料、分割區演進、漸進上線與回復。
所有容量、比例、基數和保留期都是面試假設。本題適用於資料工程師、分析平台工程師與湖倉工程師。核心能力是實體資料布局與查詢規劃,因此 category 為 data。
面試官評估重點
第一個訊號是從工作負載出發。常見述詞能讓引擎排除實體資料時,分割區欄位才有價值;欄位基數高不代表適合當分割區鍵。
第二個訊號是粒度控制。分割區太粗會多讀資料,分得太細或直接使用高基數欄位會製造中繼資料、小檔案與寫入成本。強回答會先估算每個分割區的位元組數與分割區數量。
第三個訊號是提出證據。SQL 出現日期條件不代表一定會裁剪。候選人應檢查實體執行計畫或 dry run、預計讀取的檔案與分割區、掃描位元組及結果一致性。
第四個訊號是生命週期意識。事件時間與攝取時間服務不同問題;延遲資料會改寫舊事件時間分割區。分割區演進後,舊檔案在重寫前仍保留舊規則。
回答前需要釐清的問題
- 哪些述詞必定存在且具選擇性? 如果多數查詢不帶時間,時間分割無法限制這些掃描;如果幾乎每個查詢都選一個租戶,租戶分桶的價值會提高。
- 業務依事件時間還是到達時間篩選? 事件時間符合報表語意但要接受延遲寫入;攝取時間便於追加,卻可能把同一業務日散落在多個到達分割區。
- 租戶分布是否傾斜? 固定雜湊桶能控制高基數,但一個超大租戶仍會形成熱點;只有量測後才考慮為它提供獨立路徑。
- 引擎對檔案和中繼資料有何限制? 分割區數量應受控,而且每個活躍分割區必須取得足夠位元組,才能產出合理大小的檔案。
- 資料表格式能否隱藏轉換並演進規則? 如果不能,讀寫端可能需要明確的衍生分割區欄位與遷移方案。
30 秒回答框架
「我會先分析查詢述詞。每個查詢都篩選 eventtime,所以先漸進測試 day(eventtime)。每天 6 TiB 很大;針對 35% 帶租戶等值條件的查詢,我會實測加入固定的 bucket(64, tenant_id) 轉換。它每天最多形成 64 個租戶實體群組,不會產生 12,000 個租戶分割區。
查詢使用半開事件時間範圍;我會檢查執行計畫與預計檔案數,並和未分割對照組比較掃描位元組與結果。tenant_id/hour 每天可能形成數十萬個群組,會讓寫入器拿不到足夠資料,因此直接排除。延遲事件寫進對應事件日。只有裁剪效益、檔案大小、寫入成本與中繼資料都達標,才演進 Iceberg 規則並依工作負載擴大。」
分步深入解答
第一步:把查詢日誌整理成述詞矩陣
選擇布局前先抽樣真實查詢。依工作負載記錄頻率、延遲或成本權重、時間跨度、租戶選擇性、聯結方式,以及述詞能否在掃描事實資料表前確定。只按頻率排序可能會最佳化便宜的儀表板,卻忽略每天一次但讀取大部分資料的工作。
題目給出一個強不變量:每個查詢都有事件時間範圍,因此事件時間是第一裁剪維度。租戶等值條件只出現在 35% 的查詢中,不能單獨承擔分割區維度。自由文字、指標值或高基數事件 ID 既不符合主要存取路徑,也無法形成穩定實體群組。
第二步:估算候選分割區的大小與數量
依事件日分割時,每個分割區約 6 TiB,七天查詢在檔案與 row group 裁剪前最多先落到約 42 TiB。依小時分割時,平均每個分割區約 256 GiB:
6 TiB / 24 = 0.25 TiB = 256 GiB/小時兩種粒度都足以填滿一般欄式檔案。依日約有 180 個活躍時間群組,依小時約有 4,320 個。只有大量查詢集中在幾小時內,而且實測效益超過中繼資料與寫入扇出,才選更細粒度。
直接依 tenant_id 與小時分割有危險的上限:
12,000 個租戶 * 24 小時 = 每天 288,000 個租戶小時群組稀疏資料與傾斜會讓實際數量低於上限,但估算已暴露失敗方式:每批寫入觸碰許多群組並關閉小檔案。固定的 bucket(64, tenant_id) 把每個時間分割區的租戶維度限制為 64 群。若資料均勻,每個日桶約 96 GiB;真實傾斜必須另外量測。
第三步:選擇符合工作負載的最簡單規則
先使用 day(event_time)。它服務所有查詢,中繼資料面最小。再針對租戶密集型工作負載實測以下候選:
PARTITIONED BY (day(event_time), bucket(64, tenant_id))桶數只是題目中的候選,不是通用預設值。使用真實租戶分布、目標檔案大小、寫入平行度與查詢並行,比較 16、32、64、128。只有引擎能從租戶等值條件推導桶,而且減少的規劃檔案足以抵銷中繼資料與寫入扇出,增加桶才有價值。
資料表格式支援隱藏轉換時,不要讓業務 SQL 感知實體布局。查詢只篩選邏輯欄位 eventtime 與 tenantid,由資料表中繼資料把述詞映射到實體分割區。這能避免衍生欄位不一致,也能在不重寫所有消費查詢的情況下演進規則。
第四步:讓述詞可被規劃器裁剪
將業務時區邊界轉成 UTC 後,使用明確的半開範圍:
SELECT tenant_id, event_type, COUNT(*)
FROM analytics.events
WHERE event_time >= TIMESTAMP '2026-07-01 00:00:00 UTC'
AND event_time < TIMESTAMP '2026-07-08 00:00:00 UTC'
AND tenant_id = 'tenant-42'
GROUP BY tenant_id, event_type;半開區間避免相鄰視窗在邊界重複計數。不支援的函式包住分割區來源、和另一個資料列相依欄位比較、藏在 OR 後面,或使用不一致型別轉換,都可能阻止靜態排除。不同引擎支援的轉換不同,因此要驗證計畫,不能背一套最佳化器規則。
本地曆日報表應先把指定時區當日開始與次日開始換算成 UTC。跨日光節約時間直接減固定 24 小時會出錯。儲存的事件時間、分割區轉換與報表邊界也必須採用有文件記錄的時間戳約定。
第五步:證明裁剪與結果正確
測試矩陣至少包含一小時、一天、七天、帶租戶、不帶租戶、空範圍、延遲事件與日光節約時間邊界。每個查詢記錄:
- 邏輯與實體計畫,包括分割區或檔案過濾條件;
- 預計讀取的分割區數與檔案數;
- 估算與實際掃描位元組;
- 規劃時間、執行 p50/p95 與工作數;
- 與對照組一致的資料列數、彙總值與校驗和。
未分割快照作為對照。若 180 天資料大小近似均勻,一天查詢在檔案統計生效前應只考慮約 1/180 的活躍位元組。此數值只做數量級檢查,不是承諾比例。執行計畫寫有過濾條件但仍讀取全部檔案,不能判定通過。
還要設置負對照:移除時間述詞、改成不支援的運算式,再把租戶等值改成範圍。掃描範圍應以可解釋方式擴大。負對照證明量測方法能發現裁剪缺失。
第六步:處理寫入、延遲資料與維護
分割區會改變寫入路徑。監控每個工作開啟的寫入器、每次提交產生的檔案數、檔案 p50/p90、提交延遲、中繼資料成長與衝突重試。寫入前依分割區轉換分布資料,並依每組位元組決定寫入器數量。分割區裁剪無法抵銷數百萬個小檔案。
報表依 event_time 查詢,因此延遲 48 小時的事件應寫入歷史事件日。寫入與壓實都要允許這類更新。明確定義分割區何時轉冷,並等延遲視窗關閉後再做積極重寫。回填必須限制分割區範圍、使用冪等寫入,並採用和串流攝取相同的檔案大小檢查。
第七步:避免一次性大遷移地演進
Iceberg 隱藏分割區演進後,新規則只作用於新檔案,舊檔案仍保留原規則。規劃器可以同時讀取兩者,但在熱門資料或常查歷史被重寫前,效益會混合。記錄基準快照,漸進測試最近範圍,並確認跨新舊規則的查詢都正確後再擴大。
回復包含停止以新規則寫入,並在支援時恢復舊寫入設定或資料表快照;它不會自動復原已實體刪除的檔案。快照保留與實體清理要放在灰度視窗之外。只有實測節省足以覆蓋 I/O 與衝突風險,才重寫歷史資料。
驗收條件包含業務結果一致、各工作負載依預期排除分割區、掃描位元組或延遲下降、檔案大小健康、規劃時間受控、攝取 SLO 無退步,以及中繼資料成長穩定。
高品質示範回答
「我會先取得真實述詞分布。這裡每個查詢都有事件時間,只有 35% 選擇單一租戶,因此時間是主要維度。日分割區約 6 TiB,共 180 個活躍時間群組;小時分割區約 256 GiB,但會產生 4,320 個活躍時間群組。只有窄小時查詢的效益超過額外中繼資料,我才會選小時。
基準灰度是 day(eventtime)。針對租戶密集查詢,我會測試 day(eventtime), bucket(64, tenant_id)。64 只是候選:它限制租戶扇出,資料均勻時每個日桶約 96 GiB。我會排除直接租戶小時分割,因為上限是每天 288,000 個群組,容易造成小檔案與寫入扇出。
查詢使用 UTC 半開範圍與租戶等值條件。我會查看實體計畫、預計檔案與掃描位元組,再和未分割快照比較結果及校驗和。測試涵蓋一小時、一天、七天、空範圍、延遲事件、租戶與非租戶、日光節約時間;也要用移除或遮蔽時間述詞建立負對照。
延遲事件寫回事件日,所以壓實要等 48 小時視窗後。灰度期間監控檔案分位數、工作寫入器數、規劃 p95、中繼資料、寫入延遲與衝突。Iceberg 支援分割區演進時,新檔案使用新規則,舊檔案繼續可讀。只有查詢效益超過重寫成本才處理歷史。任何結果差異、全檔案掃描、小檔案增加或攝取 SLO 退步都會停止擴大。」
常見錯誤
- 選擇基數最高的欄位 → 稀疏群組和小檔案增加,卻不服務主要述詞 → 從加權查詢述詞出發並限制實體群組數。
- 直接依租戶與小時分割 → 題目允許每天 288,000 個群組 → 先依時間,再實測固定桶轉換。
- 看到日期條件就認定裁剪 → 不支援的運算式仍可能讀取全部檔案 → 檢查實體計畫、預計檔案與掃描位元組。
- 只驗證延遲 → 快取、並行與運算資源可能偽造效益 → 同時使用掃描位元組、計畫證據、負對照與等量資源。
- 未分析就用攝取時間服務事件時間報表 → 同一業務日的延遲事件會散落在多個到達分割區 → 讓實體維度符合查詢時間語意。
- 忽略檔案大小 → 細分割區讓寫入器沒有足夠資料,把掃描成本轉成規劃成本 → 追蹤每組位元組、寫入扇出與檔案分位數。
- 立即重寫全部 1 PiB → 成本、衝突與回復暴露過大 → 灰度近期範圍,只重寫效益明確的歷史。
- 認為演進會自動重寫舊檔案 → 多套規則會共存 → 測試混合規則規劃並選擇性重寫。
追問與應對
追問一:如果 95% 的查詢都會篩選一個租戶呢?
租戶分桶的權重會明顯提高。依真實租戶分布與查詢並行重新計算桶數,比較僅時間、時間加桶,以及隔離超大租戶的方案。直接建立 12,000 個分割區仍需證明每組能形成健康檔案且中繼資料可控。
追問二:每小時也有 256 GiB,為何不直接依小時?
如果多數查詢只跨幾分鐘或幾小時,小時分割可能正確,但它會把時間群組數與寫入扇出放大 24 倍。必須依真實範圍分布比較規劃、掃描位元組、檔案大小與攝取成本。
追問三:查詢篩選本地曆日時怎麼辦?
把指定時區當日開始與次日開始轉換成 UTC instant,再使用半開範圍。這能處理日光節約時間造成的 23 或 25 小時日。也要驗證分割區轉換與儲存時間戳約定能讓引擎推導相關實體日。
追問四:計畫顯示已裁剪,但掃描位元組沒有下降怎麼辦?
檢查選中分割區是否因傾斜包含大部分資料、舊檔案是否使用其他規則、殘餘述詞是否無法利用檔案統計,以及位元組指標是否包含無關階段。比較預計檔案 ID 與未分割對照組,不能只相信計畫標籤。
追問五:如何安全地從日分割改成小時分割?
先讓新檔案漸進使用新規則,保留舊快照,並查詢跨越新舊布局的範圍。驗證正確性、規劃、檔案大小與寫入成本。只重寫效益足以覆蓋 I/O 的歷史範圍,實體清理要等回復與保留視窗結束。