Postgresql

如何“vacuumlo”一個 RDS PostgreSQL 數據庫?

  • January 13, 2019

您可能已經註意到,作為一種完全託管的數據庫即服務產品,AWS 關係數據庫服務 (RDS) 限制了使用者態命令的執行。

我正在暫存一個生產數據庫,並且我的pg_largeobject表膨脹到了整個持久性虛擬設備容量的 40%。

如何在執行 PostgreSQL 數據庫的 RDS 實例上執行(在此處vacuumlo的其他 DBA.SE 問題中有很好的解釋)?

在此處(在官方 PostgreSQL 儲存庫的 GitHub 鏡像中)找到vacuumlo我當時正在使用的 PostgreSQL 數據庫版本的來源。

你可以想像剩下的:我只是模仿程序正在執行的操作,這裡也簡單描述。

1.臨時建表。

準備對象引用或 OID 的臨時表。

1.1。臨時 lob 表作為完整 lob 表的副本。

=> SET search_path = pg_catalog;
[...]
=> CREATE TABLE vacuum_lo_removeme AS \
       SELECT oid AS lo FROM pg_largeobject_metadata;
[...]
=> ANALYZE vacuum_lo_removeme;
[...]
=> _

1.2. 限制臨時 lob 表。

執行查詢,返回所有鍵入 OID 的數據庫列:

=> SELECT
    s.nspname, c.relname, a.attname FROM pg_class c, pg_attribute a
    , pg_namespace s, pg_type t
  WHERE
    a.attnum > 0 AND NOT a.attisdropped AND a.attrelid = c.oid
    AND a.atttypid = t.oid AND c.relnamespace = s.oid 
    AND t.typname in ('oid', 'lo') AND c.relkind in ('r','m')
    AND s.nspname !~ '^pg_';

接下來,您必須對先前查詢獲取的所有結果執行此查詢(注意${VARIABLE},您應該根據自己的里程來替換),以便從臨時表中刪除實際使用的對象的所有 OID:

=> DELETE FROM vacuum_lo_removeme WHERE \
       lo IN (SELECT ${column} FROM ${SCHEMA}.${TABLE});

在我的情況下,它只有兩個表總共五列,實際上兩個表都是空的,所以這五個DELETE查詢自然沒有做任何事情。如果您有更大的啟用 OID 的子模式,您可能需要以某種方式自動執行此操作。

  1. 大對象取消連結。 ===========

最後,程序聲明了一個游標,該游標迭代lo臨時表的剩餘單元格,並通過lo_unlink函式呼叫清除它們。

2.A. 不要這樣做。

我應該使用 PLPGSQL 儲存過程來實現自動化,但由於我不擅長這類任務,所以我只發布了這個:

$ echo 'SELECT COUNT(*) FROM vacuum_lo_removeme;' | \
       $MY_AUTOMATABLE_PSQL_MILEAGE 
 count  
---------
1117233
(1 row)

然後另一個迭代在孤立 OID 的臨時表中選擇第一個孤立 OID,然後取消連結並將其從表中刪除:

$ for i in {1..1117000}; do \
       export oid=$(echo 'SELECT * FROM vacuum_lo_removeme LIMIT 1' | \
       $MY_AUTOMATABLE_PSQL_MILEAGE | grep -v 'lo\|\-\-\-\-\|row\|^$' | \
       sed s/\ //g) && \
       echo "SELECT lo_unlink($oid); \
             DELETE FROM vacuum_lo_removeme WHERE lo = $oid" | \
             $MY_AUTOMATABLE_PSQL_MILEAGE; \
       done
lo_unlink 
-----------
        1
(1 row)

DELETE 1
lo_unlink 
-----------
        1
(1 row)

DELETE 1
lo_unlink 
-----------
        1
(1 row)

DELETE 1
lo_unlink 
-----------
        1
(1 row)

DELETE 1
ERROR:  must be owner of large object 18448
DELETE 1
ERROR:  must be owner of large object 18449
DELETE 1
ERROR:  must be owner of large object 18450
DELETE 1
ERROR:  must be owner of large object 18451
[...]
lo_unlink 
-----------
        1
(1 row)

DELETE 1
[...]

我知道它被優化得非常糟糕,但我讓它慢慢地刪除那些孤兒記錄。當根本不是 DBA 時,與使用一些有意義的慣用 PLPGSQL 相比,這些內襯更容易偽造。

但這太慢了,不能這樣。

2.B。比 2.A 做得更好(雖然還不是靈丹妙藥)。

您將能夠使用簡單的方法加速大型對象的取消連結,例如:

=> CREATE OR REPLACE FUNCTION unlink_orphan_los() RETURNS VOID AS $$
DECLARE
 iterator integer := 0;
 largeoid OID;
 myportal CURSOR FOR SELECT lo FROM vacuum_lo_removeme;
BEGIN
 OPEN myportal;
 LOOP
   FETCH myportal INTO largeoid;
   EXIT WHEN NOT FOUND;
   PERFORM lo_unlink(largeoid);
   DELETE FROM vacuum_lo_removeme WHERE lo = largeoid;
   iterator := iterator + 1;
   RAISE NOTICE '(%) removed lo %?', iterator, largeoid;
   IF iterator = 100 THEN EXIT; END IF;
 END LOOP;
END;$$LANGUAGE plpgsql;

注意不需要一次取消連結大型對象100,甚至不需要一次取消特定數量x的對象,但一次取消連結100是適用於所有 AWS 實例大小的預設記憶體配置的最安全基礎。如果為此使用過大的數字,則可能會因分配的記憶體不足而導致功能失敗;您如何做將取決於要取消連結的對像數量及其大小、實例類型和大小以及應用的手動進一步配置的程度。

對於非 DBA 人來說,這有點容易偽造,然後用類似的東西呼叫它

$ for i in {0..$WHATEVER}; do echo 'SELECT unlink_orphan_los()' | \
       $YOUR_AUTOMATABLE_PSQL_MILEAGE

根據臨時大對象的表大小和您的配置允許的每個事務的鎖數,構造常量在哪裡${WHATEVER}(我正在使用 RDS 預設值,但bash我想我什至不必知道哪個是最大的)lo_unlinkRDBMS 允許使用 current的 s 數max_locks_per_transaction

3.VACUUM大對象表。

postgres 郵件列表中的這個執行緒提示,我明白我應該VACUUM pg_largeobject在取消連結對像後。

我不確定其中哪些是最低要求集,或者執行它們的適當時間,但其中一些可能會有所幫助,並且不會造成任何損害:VACUUM ANALYZE VERBOSE pg_largeobject_metadata; VACUUM ANALYZE VERBOSE pg_largeobject; VACUUM FULL ANALYZE VERBOSE pg_largeobject_metadata; VACUUM FULL ANALYZE VERBOSE pg_largeobject;當微實例取消連結對象時,我執行了幾次(花費了非常長的時間,唉),首先當大約 1/4 的對像已經被取消連結,並且一些小儲存還給作業系統時,其次當大約 1/3 的對像已經取消連結,並且另外一些小儲存被歸還給作業系統時作業系統,第三,當大約 3/5 的對像已經被取消連結時,實例經歷了大量的儲存回饋給作業系統:

在此處輸入圖像描述

在此處輸入圖像描述

我一直在尋找這種巨大的儲存回饋。執行官方首頁上的最大表查詢後,未連結的對象總數不到 3/4,對象表縮小到不到 3GiB,與最初的 20GiB 相差甚遠,甚至更臃腫。

注意對錶進行自動快速 VACUUM ANALYZE迭代具有與執行單個VACUUM FULL.

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