大表中的完全空列如何影響性能?
我在 Postgres 數據庫中有 4 億行,該表有 18 列:
id serial NOT NULL, a integer, b integer, c integer, d smallint, e timestamp without time zone, f smallint, g timestamp without time zone, h integer, i timestamp without time zone, j integer, k character varying(32), l integer, m smallint, n smallint, o character varying(36), p character varying(100), q character varying(100)
列
e
、k
和n
都是 NULL,它們根本不儲存任何值,此時完全沒用。它們是原始設計的一部分,但從未被移除。編輯 - 大多數其他列都是非空的。
問題:
- 如何計算這對儲存的影響?它是否等於列的大小*行數?
- 刪除這些空列會顯著提高此表的性能嗎?頁面記憶體是否能夠容納更多行?
由於列
e
、k
和n
可以為 NULL,我假設“100% 空”表示 NULL。**NULL 儲存很便宜。**每個 NULL “花費”空點陣圖中的一位用於儲存,否則幾乎不會影響性能。有效的儲存要求取決於每一行的空點陣圖是否已經存在並且是否還有空間容納 3 個位。
在目前實現中,具有多達8個使用者列的表在空點陣圖的元組標頭之後使用一個備用字節。再多一點,
MAXALIGN
分配另一個(通常是 8 個)字節,提供另外64列(現在總共72 個)。等等。所以對於大多數表來說,空點陣圖每行有效地花費 8 個字節,包括你的有18列的表。空點陣圖每行完全分配或根本不分配,並且僅當至少有一個實際 NULL 值時。如果定義了所有列
NOT NULL
,則永遠不會有一個。在沒有其他更重要的考慮因素的情況下,將始終(或大部分)為 NULL 的列移動到行的末尾。對性能有一點幫助。
回答問題 1。
有效的儲存要求是:
- 每行0 個字節,其他列也為 NULL。
- 每行8 個字節(通常),其中沒有其他列為 NULL,用於分配空點陣圖
回答問題 2。
放棄不會給你帶來太多好處。取決於 1,頁面記憶體將相同或略少。處理查詢、備份、原始碼等將被簡化。
進一步閱讀:
- 在 PostgreSQL 中不使用 NULL 是否仍然在標頭中使用 NULL 點陣圖?
- 將可為空的列添加到表中花費超過 10 分鐘
- 測量 PostgreSQL 表行的大小
- 為讀取性能配置 PostgreSQL
你沒有要求的
在一輪“俄羅斯方塊”之後,我建議使用這種表格佈局 - 再次排除其他更重要的考慮因素:
id serial NOT NULL , a integer , g timestamp , i timestamp , b integer , c integer , h integer , j integer , l integer , d smallint , f smallint , m smallint , o varchar(36) , p varchar(100) , q varchar(100) , k varchar(32) -- always NULL , n smallint -- always NULL , e timestamp -- always NULL
與原始佈局相比,每行至少節省 16 個字節,可能大約 20+,具體取決於您的
varchar
列。“列俄羅斯方塊”的說明在這裡:如何重新排序列
最簡單的方法是創建一個新表並將數據複製到其中。然後你得到一個沒有死列(和死行)的原始新表。您也可以對流程中行的物理順序進行排序(分群)。
也可以進行適當的更改,但是沒有方便的工具(我知道)通常可以重新排序列的順序。依賴對象,如視圖、函式(依賴)、FK 約束、索引等是這裡的限制因素。這些可能會阻止您刪除列,您必須刪除並重新創建依賴對象。
當然,對錶的並發訪問與更改它的 DDL 命令衝突。在這種情況下,您需要獨占表鎖。
刪除完全空的列並將它們添加到行的末尾既簡單又便宜。在表格中間切換填充列的順序並不簡單或便宜。我會為此創建一個新表。
這個相關的答案有更多的食譜和連結: