零停機資料庫遷移 是做什麼的?
概述
一種在滾動部署期間維持線上系統運作的資料庫結構變更規範。在發布過程中,舊版 Pod 與新版 Pod 共用同一個資料庫——因此資料庫結構必須同時相容於兩個應用程式版本。將破壞性遷移與需要它的部署綁在一起,是造成自我服務中斷的典型錯誤。
撰寫規則(所有專案)
- 兩種遷移,明確區分: 冪等的
current/集合(開發迭代——每次循環重新執行,使用CREATE OR REPLACE/IF NOT EXISTS,物件處於最終形體,不進行建立後的ALTER)以及已提交的遷移(不可變、有序、實際發布的內容)。ALTER DEFAULT PRIVILEGES是唯一允許的內聯ALTER。 - 擁有權透過遷移的執行方式決定,而非散落的
ALTER ... OWNER。 - 單一啟動來源,由本機/Docker 初始化路徑與影子/測試資料庫共用——每個環境都以相同方式建置。
擴展/收縮(並行變更)
適用情況: 資料庫中存有部署後必須保留的資料(附有數據的預備環境、正式環境)。例外: 本機/可重設專案——但撰寫已提交遷移時應視為此規則適用,以便後續升級時不必重寫。
一個資料庫結構變更應拆分為三個釋出週期,絕不能合而為一:
- 擴展 —— 以累加方式、向後相容地新增新結構:可為空的欄位、新資料表、新函式。應用程式版本 N 繼續正常運作。
- 遷移 —— 以有限批次進行逆向填充(絕不使用單一會鎖定資料表的
UPDATE);必要時由應用程式 N+1 進行雙重寫入;將讀取切換到新結構。此時才新增NOT NULL(先以NOT VALID新增限制條件,再獨立執行VALIDATE)。 - 收縮 —— 只有在應用程式 N 完全退役後,才在後續的釋出週期中移除舊的欄位或限制條件。
PostgreSQL 常見陷阱
| 陷阱 | 安全做法 |
|---|---|
含有易變/大量逆向填充預設值的 NOT NULL 欄位 | 新增可為空欄位 → 分批逆向填充 → 以 NOT VALID 新增限制條件 → VALIDATE |
| 新增限制條件 | 先以 NOT VALID 新增,再 VALIDATE CONSTRAINT(較弱的鎖) |
| 建立索引 | CREATE INDEX CONCURRENTLY — 無法在交易內執行;需與遷移工具的交易包裝隔離 |
| 被封鎖的遷移導致正式環境凍結 | 設定 lock_timeout / statement_timeout 以快速失敗 |
| 重新命名欄位 | 這是一個擴展/收縮流程:新增新欄位 → 逆向填充 → 切換讀取 → 移除舊欄位,跨越多個釋出週期 |
為什麼「最終形體 CREATE OR REPLACE」在正式環境中不夠用
該規則是開發者人體工學,在開發環境中正確。在正式環境中,面對真實使用者與滾動部署,兩個應用程式版本在發布期間共用同一個資料庫——因此擴展/收縮才是讓變更安全的方法。請勿混淆兩者。
完整理由:憲法第十七條,收錄於 engineering-constitution/references/engineering-constitution.md。部署管線:cloud-delivery-aks。