題幹與適用情境
這道題不比較所有生成欄方案,只討論把重複的列內表達式遷移為 PostgreSQL 18 虛擬生成欄。目標是統一計算語意並避免儲存生成欄造成的表重寫,同時控制讀取 CPU、函式限制與權限變化。
面試官考察重點
- 是否知道 PostgreSQL 18 新增虛擬生成欄並設為預設類型。
- 是否先證明表達式只參考目前列、使用不可變的內建函式與類型。
- 是否用雙讀對帳和真實查詢計畫驗證遷移。
- 是否理解虛擬欄在讀取時計算、不儲存,且不能作為分割區鍵。
回答前需要釐清的問題
確認表達式是否只含內建函式與類型、哪些查詢會讀取或篩選此欄、目前是否有表達式索引,以及應用能否暫時同時讀取舊表達式與新欄。也要核對角色權限,因為生成欄與基礎欄可分別授權;若要用生成欄隔離基礎欄,必須逐一驗證表達式中的函式、運算子和型別轉換是否符合 LEAKPROOF 條件。
30 秒回答架構
我會先審查表達式符合 PostgreSQL 18 虛擬欄限制,再新增明確標示 VIRTUAL 的欄位。遷移期影子讀取舊表達式和新欄,覆蓋空值、Unicode、異常輸入和歷史分割區;同時比較查詢 CPU、尾延遲與執行計畫。通過後切換讀取,保留回退舊表達式的路徑,最後才清理重複 SQL。
分步深入解析
ALTER TABLE report_events
ADD COLUMN normalized_country text
GENERATED ALWAYS AS (lower(trim(country_code))) VIRTUAL;虛擬欄在讀取時計算且不佔列儲存。表達式只能參考目前列,不可含子查詢或其他生成欄,函式必須不可變;虛擬欄也不能依賴自訂函式或類型。明確寫出 VIRTUAL 可避免團隊誤讀 PostgreSQL 18 的新預設值。
先掃描歷史資料,比較 normalized_country IS NOT DISTINCT FROM lower(trim(country_code)),覆蓋 NULL、空白、大小寫與非 ASCII 輸入。再以 EXPLAIN (ANALYZE, BUFFERS) 驗證高頻查詢,確認讀時計算沒有造成不可接受的 CPU;需要索引時另行驗證支援與寫入成本。
發布依序新增欄位、影子雙讀並以舊結果為準、差異為零且效能通過後切換。回滾只需恢復舊查詢。最後用實際角色驗證基礎欄與虛擬欄的欄級授權;若任一函式、運算子或轉換無法證明為 LEAKPROOF,就不能把虛擬欄權限視為對基礎欄的完整安全隔離。
高品質示範回答
我會把它當成查詢契約遷移。確認表達式只使用目前列的內建不可變函式後新增虛擬欄。它免除物理回填,卻把成本留在每次讀取,因此必須用生產查詢形狀驗證 CPU 和尾延遲。
應用先影子比較新舊結果,按輸入類型統計差異;一致後才切換。權限、檢視與用戶端欄位映射一起驗收。若效能或語意回歸,基礎欄仍在,可立即恢復舊表達式。
常見錯誤
- 省略
VIRTUAL,讓 DDL 意圖依賴版本預設值。 - 使用可變函式、子查詢或自訂類型後才發現不支援。
- 只測一般英文值,漏掉
NULL與 Unicode。 - 把「不佔列儲存」誤解成沒有查詢 CPU。
- 切換時刪掉舊表達式,失去快速回滾。
追問與回答
為什麼不選 STORED?
此任務要統一便宜的讀時表達式並避免物理回填。若大量掃描或排序的壓測結果不佳,才重新評估儲存生成欄。
虛擬欄能作為分割區鍵嗎?
不能。PostgreSQL 18 的生成欄不能成為分割區鍵;需要時應使用由寫入路徑維護的一般欄位。
如何驗證權限沒有擴大?
用實際應用角色分別查詢基礎欄與生成欄,核對欄級授權和表達式函式的執行權限。若生成欄承擔隱藏基礎欄的安全邊界,還要檢查每個函式,以及運算子或型別轉換背後的函式是否標記為 LEAKPROOF;PostgreSQL 不會替應用強制這項條件。只要有一條表達式路徑無法證明,就不能把生成欄授權當作完整隔離。
何時結束雙讀?
完整業務週期、歷史分割區與峰值負載都通過,差異持續為零,才停止雙讀並清理重複 SQL。