我在AWS RDS实例上的Postgres 9.5.15上运行着一个小型(〜200GB)数据仓库。
出于鲁棒性,我将新数据插入分析模式(ELT的结果),如下所示:
我知道执行删除或更新命令时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),我们需要增加它,但我首先需要一个副见。
答案 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;