如何使用 postgres 從一個巨大的表中加快插入速度?
我的數據庫中有 5 個表,大小分別為 70Gb、500Mb、400Mb、110Mb 和 20Mb。
我想創建一個包含所有表的所有列的新表,所以我嘗試了 2 個查詢,第一個是:
select into new_table as select .. from t1 , t2 , t3 , t4 ,t5 where t1.id2 = t2.id and t1.id3 = t3.id and t1.id4 = t4.id and t1.id5 = t5.id
第二個是:
insert into new_table select .. from t1 , t2 , t3 , t4 ,t5 where t1.id2 = t2.id and t1.id3 = t3.id and t1.id4 = t4.id and t1.id5 = t5.id
在對我的大數據表執行這兩個查詢之前,我在一個總共 1G 的數據庫上都進行了嘗試,第一個只用了 7 秒,第二個大約 1000 萬。
現在,在我的龐大數據庫上執行第一個,即使在執行查詢之前我有 250Gb 的可用空間,我的磁碟也已滿,並且沒有完成查詢,所以我得到了以下錯誤:
ERROR: could not write to temporary file: No space left on device
第二個是花費大量時間並緩慢消耗我的可用磁碟空間,並且作為第一個,不返回結果。
這兩個查詢有什麼區別?有沒有辦法使插入成為非事務性的,以便我可以按照我的插入步驟進行操作。而且我猜 Postgres 使用日誌(日誌)所以有沒有辦法停用它以加快插入速度?或者我應該遵循另一種方法以獲得所需的結果而不填滿所有磁碟。
Ps:沒有觸發器,每個表只有一個主鍵。
.. 一個包含所有表的所有列的新表..
真的嗎?這不太可能有用。
選擇 .. 從 t1 加入 t2 加入 t3 加入 t4 …
如果沒有“on”子句來告訴 postgres如何將表放在一起,那些“連接”會將t1 中的每一行連結到 t2中的每一行**,然後將其中的每一行連結到 t3 中的每一行,然後將每一行連結到 t3中的每一行t4 …
這是非常多的“行”。
與往常一樣,建構一個返回所需數據的選擇語句,然後只需在其前面添加“插入”位。
如果你的桌子都是相同的結構,我懷疑‘union 會更好地為你服務
$$ all $$’s 而不是 ‘join’s 但 YMMV; 我們無法確定,因為您沒有向我們提供表格結構或預期結果。
這些查詢之間的區別在於,第一個查詢失敗時,將導致 new_table 不存在,因此,與該表關聯的所有底層數據文件將立即取消連結(本質上是 rm’ed),而對於第二個查詢,即使在插入失敗後該表仍將存在(因為 CREATE TABLE 是一個單獨的語句)並且這些文件仍然存在,儘管其中充滿了從未送出的行。
結果表的總大小將是相同的;可以安全地假設您的 INSERT 也會用完空間,除非發生這種情況時,不會立即刪除充滿未送出行的表文件。您需要刪除表以使該空間再次可供作業系統使用 - autovacuum可能會在短時間內這樣做,但也可能不會,這取決於一系列因素。請注意,如果您的事務日誌與您正在執行插入的表空間位於同一卷上,您的數據庫可能會停止執行,從而阻止您執行 DROP TABLE。(這是否真的發生取決於抽獎的事情,例如數據庫是否需要為事務日誌分配更多空間以添加執行 DROP TABLE 的事務。)
編輯:在避免事務日誌的事情上,查看UNLOGGED 表。請注意,執行 ALTER TABLE … SET LOGGED 將需要再次重寫整個表(或者至少幾年前這樣做了,我已經有一段時間沒有使用 UNLOGGED 了。)
編輯 2:至於為什麼第二種形式要慢得多,這是因為 CREATE TABLE AS(和 SELECT INTO)可以在預設配置中完全跳過 WAL 日誌記錄。見https://www.postgresql.org/docs/12/runtime-config-wal.html#GUC-WAL-LEVEL