題幹與適用場景
PostgreSQL 18 預設使用虛擬生成欄。生成欄由其他欄位計算,不能直接寫入。虛擬欄在讀取時計算且不佔表儲存;儲存欄在寫入時計算並佔用儲存。選擇會改變寫入成本、讀取成本、索引能力和遷移行為。
假設原始事件寫入後不可變,報表頻繁讀取衍生值,表規模足以讓重寫或讀取 CPU 突增成為風險。
面試官考察點
面試官關注語義區分是否準確、是否知道表達式必須逐行且符合不可變限制,以及是否根據工作負載選擇。強回答會討論索引、查詢計畫、回填、回滾,並判斷生成欄是否比檢視表或物化檢視更合適。
普通回答只說「虛擬省磁碟,儲存更快」。強回答會說明 CPU 成本移到哪裡、表達式限制如何影響方案,以及如何驗證遷移前後結果一致。
回答前需要釐清的問題
- 讀寫比例是多少?哪些衍生欄位位於關鍵查詢路徑?
- 衍生值是否需要索引、約束、分割,或複製到其他系統?
- 來源欄位是否可變?表達式能否保持不可變且只依賴目前列?
- 表有多大?可接受的鎖和重寫預算是多少?
- 用戶端依賴實體儲存,還是只依賴 SQL 結果?
如果值很少讀取且計算昂貴,儲存欄可以把成本移到寫入。如果邏輯有跨列依賴或不符合不可變限制,生成欄不適合,應使用檢視表、觸發器或資料管道。
30 秒回答框架
「我會先看工作負載和表達式限制。虛擬欄不佔儲存、不在寫入時計算,但在讀取時付出 CPU;儲存欄在寫入時付出成本並佔空間,有利於高頻讀取和索引。我會把虛擬欄用於便宜且低頻的衍生,把儲存欄用於熱點、昂貴或需要索引的值,先驗證 PostgreSQL 18 的限制,再用影子欄比較計畫和結果,並保留回滾路徑。」
分步驟深入解答
- 確認語義。 驗證表達式只使用目前列和允許的不可變或內建函式。生成欄不能直接寫入,也不能引用另一個生成欄。
- 測量工作負載。 估算每種方案的寫入放大、讀取 CPU、儲存、快取壓力和索引收益。
- 選擇實體行為。 寫入延遲和儲存更緊張、讀取計算便宜時選虛擬;讀取熱點、計算昂貴或需要索引時選儲存。
- 檢查邊界。 虛擬欄對用戶自訂型別和函式有限制;生成欄也不能作為分割鍵。確認複製和 ORM 反射行為。
- 安全遷移。 增加影子生成欄,在抽樣和對抗資料上與舊表達式比較,切換用戶端前檢查計畫變化。
- 營運和回滾。 監控讀取 CPU、寫入延遲、表和索引大小、空值或錯誤率、結果不一致,並保留舊投影直到新欄驗證完成。
替代方案包括便宜投影使用普通檢視表,跨列計算使用物化檢視,需要下游持久值時使用 ETL 欄。關鍵是明確衍生資料的所有權。
高品質示範回答
「標準化國家代碼是確定性的逐列計算,每個報表查詢都會讀取。我會測試儲存生成欄,因為在寫入時付出一次成本,可以減少重複解析並支援需要的存取模式索引。很少查詢的診斷標籤則保留虛擬。遷移前增加影子欄,在包含空值、邊界和非法輸入的資料上比較結果,檢查計畫,觀察寫入延遲和索引增長;若 CPU 或儲存超預算,就讓用戶端回到舊投影,並重新評估物化檢視。」
常見錯誤
- 錯誤表現: 認為虛擬欄總是更快 → 失敗原因: 高频讀取會反覆消耗 CPU → 修正方法: 測量讀取頻率和表達式成本。
- 錯誤表現: 假設所有表達式都允許 → 失敗原因: 存在逐列和不可變限制 → 修正方法: 先驗證表達式與型別。
- 錯誤表現: 不比較就直接切換 → 失敗原因: 空值和邊界語義可能漂移 → 修正方法: 使用影子欄和不一致檢查。
- 錯誤表現: 忽略索引和儲存 → 失敗原因: 讀取優化可能製造寫入或磁碟壓力 → 修正方法: 監控計畫、索引大小和寫入延遲。
追問及應對
如果表達式呼叫用戶自訂函式怎麼辦?
檢查欄位是虛擬還是儲存,並確認函式符合文件限制。虛擬欄可能禁止用戶自訂函式和型別;必要時改為儲存欄、檢視表或資料管道。
生成欄可以作為分割鍵嗎?
不可以。分割應單獨設計;需要分割裁剪時,明確物化一個受支援的鍵。
如何證明遷移等價?
在代表性樣本以及空值、邊界、非法輸入和時區場景上比較新舊表達式,切換讀取前對不一致告警。
什麼時候物化檢視更好?
當計算跨列或跨表、可以接受刷新延遲,並且需要獨立刷新與索引時使用物化檢視。生成欄適合確定性的逐列衍生。