題幹與適用場景
一個 PostgreSQL 18 發布端要把部分租戶和部分欄位複製到訂閱端,訂閱端用於報表和區域服務。請解釋 row filter 與 column list 的語義、UPDATE 進入或離開過濾集合時的轉換、初始同步、分割表行為,並設計安全與運維方案。
PostgreSQL 文件說明,row filter 在發布前判斷資料列是否滿足條件;column list 控制複製欄位但不是安全邊界。面試重點是複製語義、資料一致性與變更治理,不能只回答「在 publication 上加 WHERE」。
面試官考察點
面試官會看你能否說明過濾表達式由複製連線角色執行、false 或 NULL 不複製、TRUNCATE 不受影響;能否解釋 UPDATE 舊資料列和新資料列分別匹配時的 UPDATE/INSERT/DELETE 轉換;能否處理 replica identity、初始同步、多個 publication 的 OR 語義、分割根與欄列表演進;能否識別 column list 不能取代保密控制。
回答前需要釐清的問題
複製目標
確認訂閱端需要哪些租戶、操作類型與欄位,是否允許初始快照,是否需要雙向寫入,以及訂閱端版本是否低於 15 或 18。
身份與過濾
確認表的 replica identity、過濾欄位是否會更新、分割是否使用根表發布,以及多個 publication 是否覆蓋同一張表。
安全與恢復
確認敏感欄位是否必須完全不可見、複製連線角色權限、網路隔離、重建訂閱與過濾規則誤配時的回滾窗口。
30 秒回答框架
「row filter 決定哪些資料列的變更進入發布,false 或 NULL 會丟棄該變更;column list 只減少複製欄位,不能當作安全邊界。UPDATE 要同時評估舊資料列和新資料列:未匹配到匹配變成 INSERT,匹配到未匹配變成 DELETE,雙方匹配才是 UPDATE。過濾涉及 UPDATE/DELETE 時,表達式欄位和 column list 必須覆蓋 replica identity。上線前我會凍結 publication 變更,驗證初始同步、分割表、多個過濾器 OR 合併和舊版本行為,並把敏感資料保護放在發布端權限與 view 層。」
分步驟深入解答
第一步:定義發布不變量
為每張表記錄允許的租戶集合、操作類型、欄位集合、過濾表達式與訂閱端版本。明確目標是效能裁剪、行為隔離還是安全保護;安全目標不能只依賴 column list。
第二步:設計 row filter
過濾表達式在發布前執行,使用 replication connection 對應角色;只允許文件規定的簡單表達式。表達式為 false 或 NULL 時不複製,TRUNCATE 不受過濾影響。發布 UPDATE/DELETE 時,過濾欄位必須由 replica identity 覆蓋;僅發布 INSERT 時可使用其他欄位。
第三步:推導 UPDATE 轉換
更新前後都計算過濾條件。舊資料列和新資料列都匹配時傳送 UPDATE;都不匹配時不傳送;只有新資料列匹配時向訂閱端傳送 INSERT;只有舊資料列匹配時傳送 DELETE。訂閱端因此保持「目前滿足過濾條件的資料列集合」,而不是簡單重播所有 UPDATE。
第四步:限制 column list
column list 要包含 replica identity 所需欄位,訂閱表至少具備所有發布欄位。列表順序不影響複製。沒有列表表示未來新增欄位也會自動複製;明確列出全部欄位則不會自動包含後續新增欄位。多個 publication 對同表使用不同欄列表可能導致訂閱無法恢復,應在變更前檢查。
CREATE PUBLICATION tenant_eu
FOR TABLE orders (order_id, tenant_id, status, total)
WHERE (tenant_id = 'eu');第五步:處理初始同步
初始同步會按 row filter 複製符合條件的既有資料列,按 column list 複製選定欄位。但早於 15 的訂閱端可能複製整表,早於 18 的版本對 generated column 也有額外限制。切換前要檢查版本矩陣與快照期間的寫入積壓。
第六步:處理分割與多個 publication
publishviapartition_root=true 時使用根分割表的過濾器或欄列表;預設 false 時按各分割定義處理。同一表在多個 publication 中的 row filter(同一 publish 操作)按 OR 合併,未過濾的 publication 會使其他過濾器失去裁剪效果。
第七步:建立運維護欄
把 publication 定義納入 migration 審查,記錄過濾命中率、複製延遲、衝突、初始同步列數與規則版本。變更前在影子訂閱驗證舊/新資料列邊界;發現洩露或漏數時暫停訂閱、修正 publication,再按一致性檢查結果重建。
高品質示範回答
我會把 row filter 當作發布集合定義,把 column list 當作傳輸裁剪。過濾表達式由複製連線角色執行,false/NULL 與 TRUNCATE 的語義要單獨驗證。UPDATE 必須比較舊資料列、新資料列,跨邊界時轉換為 INSERT 或 DELETE;涉及 UPDATE/DELETE 時保證過濾欄位與發布欄位覆蓋 replica identity。初始同步、分割根與多個 publication 的 OR 合併都要納入測試。敏感資料仍由發布端權限、view 或獨立脫敏管道保護,column list 只作為效能與資料形狀控制。
常見錯誤
- 錯誤表現: 認為 column list 能阻止惡意訂閱者讀取未發布欄位。→ 失敗原因: 官方文件明確它不是安全機制。→ 修正方法: 在發布端權限、view 或脫敏層實施保密控制。
- 錯誤表現: 只按新資料列判斷 UPDATE。→ 失敗原因: 資料列可能離開或進入過濾集合。→ 修正方法: 評估舊/新兩次並按四種結果轉換。
- 錯誤表現: 忽略 replica identity。→ 失敗原因: UPDATE/DELETE 無法安全定位舊資料列。→ 修正方法: 檢查身份欄位是否包含在過濾表達式與 column list 中。
- 錯誤表現: 多個 publication 各自看起來很窄就認為總複製量很小。→ 失敗原因: 同一 publish 操作的過濾器會 OR 合併。→ 修正方法: 計算合併後集合並拒絕無過濾覆蓋。
追問及應對
什麼時候用 row filter,什麼時候拆 publication?
租戶或區域邊界穩定且表達式簡單時可用 row filter;若權限、生命週期與運維責任不同,拆成獨立 publication 與訂閱更容易審計。兩者都要評估初始同步與規則變更成本。
新增欄位怎樣避免意外複製?
使用明確 column list 並在 schema 變更審查中更新列表;不要把「明確列出目前全部欄位」和「未指定列表」混為一談,後者會自動包含未來欄位。
如何驗證 UPDATE 跨邊界?
構造舊不匹配/新匹配、舊匹配/新不匹配、雙方匹配與雙方不匹配四組更新,分別核對訂閱端 INSERT、DELETE、UPDATE 與無事件結果。
過濾能否取代多租戶安全隔離?
不能。它是邏輯複製的資料選擇機制;敏感資料隔離仍需要發布端最小權限、獨立憑證、網路控制與必要的脫敏。