Postgresql

將可為空的列添加到表中花費超過 10 分鐘

  • January 14, 2015

我在表格上添加新列時遇到問題。

我嘗試執行了幾次,但是執行了 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可能會有所幫助。對大表和鎖定細節進行了多項改進。但是,如果您的數據庫中有問題,您可能應該首先弄清楚這一點。

引用自:https://dba.stackexchange.com/questions/78405