將可為空的列添加到表中花費超過 10 分鐘
我在表格上添加新列時遇到問題。
我嘗試執行了幾次,但是執行了 10 多分鐘後,由於鎖定時間,我決定取消查詢。
ALTER TABLE mytable ADD mycolumn VARCHAR(50);
有用的資訊:
- PostgreSQL 版本:9.1
- 行數:~ 250K
- 列數:38
- 可空列數:32
- 約束數量:5(1 PK,3 FK,1 UNIQUE)
- 索引數:1
- 作業系統類型:Debian Squeeze 64
我發現了有關 PostgreSQL 管理可空列的方式的有趣資訊(通過 HeapTupleHeader)。
我的第一個猜測是,因為這個表已經有 32 個 8-bits 的可空列
MAXALIGN
,所以 HeapTupleHeader 的長度是 4 字節(未驗證,我不知道該怎麼做)。因此,添加一個新的可為空列可能需要在每一行上更新 HeapTupleHeader 以添加一個新的 8-bits
MAXALIGN
,這可能會導致性能問題。因此,我嘗試更改其中一個可空列(實際上並不是真正可空的),以將可空列的數量減少到 31,以檢查我的猜測是否正確。
ALTER TABLE mytable ALTER myothercolumn SET NOT NULL;
不幸的是,這個alter也需要很長時間,超過5分鐘,所以我也放棄了它。
您是否知道可能導致這種性能成本的原因?
這裡有幾個誤解:
空位圖不是堆元組標頭的一部分。根據文件:
有一個固定大小的標頭(在大多數機器上佔用 23 個字節),然後是一個可選的空點陣圖 …
您的 32 個可為空的列是不值得懷疑的,原因有兩個:
- 每行添加空點陣圖,並且僅當行中至少有一個實際
NULL
值時。可空列沒有直接影響,只有實際NULL
值有影響。如果分配了空點陣圖,則始終完全分配(全部分配或全部分配)。空點陣圖的實際大小是每列 1 位,向上舍入到下一個字節。每個目前的原始碼:#define BITMAPLEN(NATTS) (((int)(NATTS) + 7) / 8)
- 空點陣圖在堆元組頭之後分配,後跟可選的 OID,然後是行數據。OID 或行數據的開始
t_hoff
在標題中指示。每條評論原始碼:請注意,t_hoff 必須是 MAXALIGN 的倍數。
- heap tuple header後面有1個空閒字節,佔用23個字節。因此,最多8列的行的空點陣圖實際上無需額外成本。對於表中的第 9 列,再增加
t_hoff
一個MAXALIGN
(通常為 8 個)字節以提供另外 64 個列。所以下一個邊界將是72列。要顯示 PostgreSQL 數據庫集群(包括
MAXALIGN
)的控制資訊,例如在 Debian 機器上典型安裝 Postgres 9.3:sudo /usr/lib/postgresql/9.3/bin/pg_controldata /var/lib/postgresql/9.3/main
我更新了您引用的相關答案中的說明。
除此之外,即使您的
ALTER TABLE
語句觸發了整個表的重寫(它可能會更改數據類型),250K 確實不算多,並且在任何一台體面的機器上都只是幾秒鐘的事情(除非行異常大) . **10 分鐘或更長時間表示完全不同的問題。**您的語句很可能正在等待鎖定表。越來越多的條目
pg_stat_activity
意味著更多打開的事務 - 表明對錶的並發訪問(很可能)必須等待操作完成。黑暗中拍了幾張
檢查可能的表膨脹,嘗試溫和
VACUUM mytable
或更激進的VACUUM FULL mytable
方法——這可能會遇到相同的並發問題,因為這種形式也獲得了排他鎖。您可以嘗試pg_repack代替…我將首先檢查索引、觸發器、外鍵或其他約束可能存在的問題,尤其是那些涉及列的問題。特別是可能涉及損壞的索引?嘗試
REINDEX TABLE mytable;
或全部嘗試,然後在同一事務中DROP
重新添加它們。ALTER TABLE
嘗試在夜間或負載不多時執行該命令。
蠻力方法是停止對伺服器的訪問,然後重試:
如果無法確定,升級到目前版本或即將推出的 9.4可能會有所幫助。對大表和鎖定細節進行了多項改進。但是,如果您的數據庫中有問題,您可能應該首先弄清楚這一點。