引言:高負載系統面臨的 DDL 鎖定困境
在許多快速迭代的互聯網與企業級後端系統中,資料庫往往承受著每秒數千次交易的讀寫壓力。當業務需要修改關鍵資料結構——例如將既有的使用者餘額由整數改為高精度定點數,或者需要拆分龐大的單體訂單明細表時,傳統的 `ALTER TABLE` 語法會嘗試獲取資料庫最高的 `ACCESS EXCLUSIVE` 鎖定。在高併發場景下,這會瞬間阻擋所有後續的讀寫查詢,導致資料庫連線池在數秒內耗盡,進而引發整體的後端服務雪崩。
核心解法:Expand and Contract 模式解析
Expand and Contract(擴展與收縮)模式是分散式與高併發架構中最經典的演化策略。其核心思維是將一次性的破壞性變更,拆解為五個獨立且彼此相容的漸進步驟: 1. 擴展期 (Expand):在現有資料表中新增 nullable 的新欄位或建立新版影子表 (Shadow Table),此時舊程式碼僅讀寫舊欄位,新結構完全不影響現有生產流量。 2. 雙寫期 (Dual Write):更新後端應用層,在寫入新資料時同時寫入舊欄位與新欄位;此時讀取仍以舊欄位為主。 3. 歷史資料回補 (Backfill):在背景透過限制批次大小(如每次 1,000 筆並間隔短暫 sleep)的方式,將歷史舊資料轉換並補齊至新欄位,避免產生長時間長交易與磁碟 I/O 阻塞。 4. 切換讀取 (Switch Read):當資料校驗確認 100% 一致後,將後端讀取邏輯切換至新欄位或新表。 5. 收縮期 (Contract):在確保新結構穩定運行數週無誤後,安全刪除舊欄位或歷史廢棄表,完成整個重構循環。
PostgreSQL 中的實戰防禦:lock_timeout 與並發索引
在實際執行 DDL 時,即便僅是新增欄位或建立索引,也必須設定嚴格的防禦參數。例如,在執行任何 DDL 交易前務必宣告 `SET lock_timeout = '2s';`,確保一旦在 2 秒內無法獲取鎖定即立即自動中斷放棄,而不至於堆疊後續交易隊列。 此外,建立索引必須始終採用 `CREATE INDEX CONCURRENTLY`,以避免鎖定資料表的寫入操作。在資料庫重構的現場,架構師的職責不僅是設計優雅的結構,更是為每一次 DDL 變更鋪設嚴密的退路與安全防護網。