Postgresql

UPDATE 是否為未更改的 TOASTed 值寫入新行版本?

  • March 17, 2022

我正在使用一個帶有大 TEXT 欄位的 PostgreSQL 表,理論上它會定期更新。我曾考慮將數據直接儲存在文件系統中,但使用 TOAST,數據已經在頁面外儲存並壓縮在數據庫中,所以我想我會保持簡單,只使用數據庫儲存。

為了提供一些上下文,我正在索引 RSS 提要。我將每 24 小時執行一次腳本,該腳本會提取 RSS 提要並可能更新表格。這會導致大量死元組,從而導致磁碟上使用大量空間。當然,autovacuum 最終會解決這個問題,但它有可能包含大量數據(很多 GB),我想確保我知道當我在這個非常大的表上進行大量更新時會發生什麼。

我的一個解決方案是僅在提要有某些實質性更改時才更新 TEXT 欄位(儲存 RSS 數據),例如網站上的新文章。這意味著除非我真的必須這樣做,否則我可以避免執行 UPDATE 。但是,我仍然想更新表(以跟踪我最近一次執行 HTTP 請求的時間)。這將使用舊版本的行數據創建一個死元組。

如果 TEXT 數據沒有實際更改,會發生什麼情況?當 UPDATE 創建一個死元組時,它還會複製 TEXT 數據嗎?或者 TEXT 數據是否會保持原樣,因為它沒有更改並且儲存在頁面外?

這是 Postgres 的 MVCC 模型中的一個主要捷徑UPDATE(與DELETE+相比INSERT):離線儲存(TOASTed)且未更改的欄位UPDATE保持原樣。意思是,主關係中的舊(即將死)行版本和新行版本指向相同的 TOASTed 值,那裡沒有額外的膨脹

或者正如手冊所說

UPDATE操作期間,未更改欄位的值通常保持原樣;因此UPDATE,如果沒有任何外線值發生變化,則具有外線值的一行不會產生 TOAST 成本。

正如 a_horse_with_no_name 指出的那樣:

“未更改UPDATE”,或手冊所說的“未更改”,意味著“未針對”的SET子句UPDATE。Postgres 不會驗證新列的值是否實際上與之前的行版本不同。

WHERE如果可能,請通過添加子句跳過開頭不變的行。這仍然會使案例同時更新多個列,而其中一些列保持不變。如果這適用於您的大專欄,則可能需要單獨更新它,並且僅在它實際發生變化的地方進行更新。看:

OTOH,如果您定期更新大的 TOASTed 欄位,請考慮為這些列使用LZ4 壓縮算法(Postgres 14 中的新算法)。磁碟佔用空間更大,但性能要好得多。看:

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