Postgres的表膨胀

时间:2019-12-23 14:07:51

标签: postgresql database-administration soft-delete

我在AWS RDS实例上的Postgres 9.5.15上运行着一个小型(〜200GB)数据仓库。

出于鲁棒性,我将新数据插入分析模式(ELT的结果),如下所示:

  1. 插入新切片
  2. 使用删除命令删除旧切片
  3. 真空

我知道执行删除或更新命令时Postgres软删除元组。给出表的大小不必担心。问题在于,死元组不会在(3)上的显式真空或常规自动真空下除去。因此,如果流水线执行了很多次,我最终会遭受巨大的表膨胀,这会极大地影响表性能,更不用说浪费了额外的存储空间。

此外,当我开始调查时,发现甚至系统表也存在此问题:

schemaname | relname | n_live_tup | n_dead_tup | ratio%
pg_catalog | pg_attribute | 46081 | 8339587 | 18097
pg_catalog | pg_depend | 27375 | 2490507 | 9097
pg_catalog | pg_statistic20094 | 1208474 | 6013

这可能会以我什至无法想象的方式使实例的总体性能变差。当我尝试做VACUUM FULL VERBOSE pg_catalog.pg_attribute时,它会给我以下信息:

"pg_attribute": found 0 removable, 8387117 nonremovable row versions in 152494 pages

我已经阅读了“表膨胀的3个原因”之类的文章,但均不适用(我没有进行复制,没有挂起的事务,等等)。我可以使用pg_repack之类的东西来按时消除肿胀,但我想了解其发生的原因。我也不想肯定要重新打包系统表。

我唯一的假设是,真空要求所有无效的元组都适合内存,内存限制为maintenance_work_mem设置(对于我们的实例为127MB),我们需要增加它,但我首先需要一个副见。

1 个答案:

答案 0 :(得分:1)

我最终编写了自己的函数来重新打包数据并按计划运行它们:

-- repack an individual table
CREATE OR REPLACE FUNCTION admin.repack_table(text)
RETURNS text
AS $$
DECLARE SQL text;
BEGIN

    SELECT
     'CREATE TEMP TABLE t1 (LIKE '||$1||');'||chr(10)||
     'INSERT INTO t1 SELECT * FROM '||$1||';'||chr(10)||
     'TRUNCATE TABLE '||$1||';'||chr(10)||
     'INSERT INTO '||$1||' SELECT * FROM t1;'||chr(10)||
     'DROP TABLE t1;'||chr(10)||
     'ANALYZE '||$1||';'
    INTO SQL;
    EXECUTE SQL;
    RETURN $1;
END;
$$ LANGUAGE plpgsql;

-- repack all tables in certain schema (with an optional threshold for N of dead tuples)
CREATE OR REPLACE FUNCTION admin.repack_schema(text,int default 5000)
RETURNS table (table_name text)
AS $$
DECLARE SQL text;
BEGIN
RETURN QUERY (
    with
     schema as (select $1)
    select admin.repack_table(t.table_schema||'.'||t.table_name)
    from information_schema.tables t
    where t.table_schema=(select * from schema)
    and t.table_name in (
        select relname
        from pg_stat_all_tables
        where schemaname=(select * from schema)
        and n_dead_tup>$2
        and n_live_tup<1000000 -- avoid repacking too large tables
    )
);
END;
$$ LANGUAGE plpgsql;