如何`vacuumlo` RDS PostgreSQL 数据库?

483*_*347 4 postgresql vacuum blob amazon-rds

您可能已经注意到,作为一种完全托管的数据库即服务产品,AWS 关系数据库服务 (RDS) 限制用户空间命令的执行。

我暂存了一个生产数据库,结果我的pg_largeobject表膨胀到了整个持久性虚拟设备容量的 40%。

如何vacuumlo在运行 PostgreSQL 数据库的 RDS 实例上运行(在此处的其他 DBA.SE 问题中得到很好的解释)?

483*_*347 5

发现这里(官方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;
[...]
=> _
Run Code Online (Sandbox Code Playgroud)

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_';
Run Code Online (Sandbox Code Playgroud)

接下来,您必须对先前查询获取的所有结果执行此查询(注意${VARIABLE},您应该根据自己的里程数自行替换),以便从临时表中删除实际使用的所有对象的 OID:

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

在我的情况下,只有两个表总共五列,实际上两个表都是空的,所以五个DELETE查询自然没有做任何事情。如果您有一个更大的 OID 启用子模式,您可能需要以某种方式自动执行此操作。

2. 大对象解除链接。

最后,程序声明一个游标,它迭代lo临时表的剩余单元格,并通过lo_unlink函数调用清除它们。

2.A. 不要这样做。

我应该用一个 PLPGSQL 存储过程来自动化它,但是由于我对这种任务很不擅长,所以我只发布了一个 liner:

$ echo 'SELECT COUNT(*) FROM vacuum_lo_removeme;' | \
        $MY_AUTOMATABLE_PSQL_MILEAGE 
  count  
---------
 1117233
(1 row)
Run Code Online (Sandbox Code Playgroud)

然后另一个迭代选择孤儿 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
[...]
Run Code Online (Sandbox Code Playgroud)

我知道它被优化得非常糟糕,但我让它慢慢地删除了那些孤立的记录。当完全不是 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;
Run Code Online (Sandbox Code Playgroud)

注意,不需要一次取消链接大对象100,甚至不需要一次取消链接特定数量x的对象,但一次取消链接100是适用于所有 AWS 实例大小的默认内存配置的最安全基础。如果为此使用过大的数字,则可能会因分配的内存不足而导致功能失败;您如何做将取决于要取消链接的对象数量及其大小、实例类型和大小,以及应用手动进一步配置的程度。

对于非 DBA 人员来说,这有点容易伪造,然后用类似的东西调用它

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

${WHATEVER}根据临时大对象的表大小和您的配置允许的每个事务的锁数,构造常量在哪里(我使用的是 RDS 默认值,但bash我想我什至不必知道哪个是最大的lo_unlinkRDBMS 允许使用当前 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,但不获取排他锁。