1. 題目
生產表持續接收訂單寫入,查詢團隊希望新增複合索引;另一張表的索引膨脹,需要重建。表規模很大,發布窗口只有低峰期,且不能阻塞一般讀寫。請給出可回滾、可觀測的線上方案。
2. 約束與澄清
- 先確認 PostgreSQL 版本、資料表與索引類型、主從拓撲、磁碟餘量及寫入峰值。
- 區分
CREATE INDEX、CREATE INDEX CONCURRENTLY、REINDEX CONCURRENTLY的適用場景。 - 並行建立可減少寫入鎖影響,但會多次掃描資料表、佔用 CPU/IO,且不能放在交易區塊中。
- 先定義可接受的建立時間、鎖等待、延遲與失敗後的清理窗口。
3. 核心思路
一般建立索引可能阻塞寫入;並行建立允許持續插入、更新與刪除,但需要更長時間與更多資源。應先在影子環境用真實規模估算,生產執行時設定 locktimeout、statementtimeout 與資源監控。索引完成後還要確認規劃器選擇、回滾路徑與副本一致性,不能把指令成功當成業務成功。
4. 參考流程
text
preflight:
verify_version_replicas_disk_and_query_shape()
estimate_scan_cost_on_shadow_copy()
reserve_maintenance_window_and_abort_thresholds()
build:
set lock_timeout = short
set statement_timeout = bounded
CREATE INDEX CONCURRENTLY idx_orders_customer_time
ON orders (customer_id, created_at DESC)
verify:
inspect_index_state_and_size()
EXPLAIN (ANALYZE, BUFFERS) representative_queries()
compare_write_latency_replica_lag_and_error_rate()重建時優先使用 REINDEX CONCURRENTLY;若失敗留下無效的暫存索引,先依文件識別並清理,再重新嘗試。部署腳本要保證命名、冪等與告警可追蹤,不能把並行 DDL 藏在一般交易遷移裡。
5. 失敗場景與取捨
並行建立可能因長交易、衝突快照或磁碟不足失敗;失敗物件可能保持無效狀態,繼續佔用空間。建立期間 CPU、IO 與 WAL 增長會拖慢業務並拉大複製延遲,因此要限速或暫停。若無法接受建立成本,可以先優化查詢、分區或使用線上遷移工具,但工具仍需驗證觸發器、回填、切換與回滾行為。
6. 驗證與觀測
- 記錄索引狀態、大小、建立耗時、鎖等待、WAL、CPU/IO 與副本延遲。
- 對代表性查詢比較執行計畫、掃描行數、p95/p99 延遲與寫入吞吐。
- 檢查長交易、無效索引、重複索引及約束依賴。
- 觀察多個完整業務高峰後再刪除舊索引,並保留復原腳本。
7. 常見誤區
- 以為
CONCURRENTLY完全不拿鎖,忽略短暫鎖等待與資源競爭。 - 把
CREATE INDEX CONCURRENTLY放進交易區塊,導致指令直接失敗。 - 只看建立索引指令成功,不檢查無效索引、複製延遲與實際計畫。
- 沒有磁碟、WAL 與長交易預算就直接對超大表重建。
8. 面試評分點
能區分並行 DDL 語義
應說明一般與並行建立、重建索引的鎖、掃描次數、交易限制與資源代價。
能規劃生產前置檢查
應檢查版本、磁碟、長交易、複製拓撲、查詢形狀,並先用接近真實規模的資料估算。
能設計失敗復原
應處理無效索引、逾時、磁碟不足與複製落後,說明清理、重試與回滾步驟。
能用業務指標驗證
應比較執行計畫、p95/p99、寫入延遲、WAL、鎖等待與副本延遲,而不是只看 DDL 回傳碼。