我的PostgreSQL服务器上有一些表,它们占用的磁盘空间比它们应该多得多。我知道完全真空可以回收空间,但是完全真空会在表上独占锁定,并且这些表会不断写入,完全真空会导致过多的服务中断(其中一个表占用了超过TB的额外空间) )。因此我想出了以下功能来重新创建表而不锁定它。
create or replace function recreate_table(t text) returns void as $$
declare
new_name text;
old_name text;
seq_name text;
tab regclass;
begin
new_name := 'new_' || t;
old_name := 'old_' || t;
seq_name := t || '_id_seq';
execute format('create table %I (like %I including all)', new_name, t);
execute format('alter table %I rename to %I', t, old_name);
execute format('alter table %I rename to %I', new_name, t);
execute format('alter sequence %I owned by %I.id', seq_name, t);
execute format('insert into %I select * from %I', t, old_name);
--- execute format('drop table %I', old_name);
end;
$$ LANGUAGE plpgsql;
它似乎按预期工作,但是我不知道这有多安全。我没有看到这种方法有什么问题吗?
注意:我使用插入是因为有问题的表包含日志数据,因此它们会不断地附加到日志中,但是现有记录永远不会改变。为了正确地做到这一点,我将使用插入和更新的某种组合,但是我的postgres是9.4,并且不支持upserts,并且使用插入对于我的目的已经足够了。
更新:看来alter table
独占了表的锁定,并且该锁定一直持续到函数完成为止,因此在执行插入操作时表保持锁定状态。而且,显然PostgreSQL本身并不支持函数内部的提交。使用dlink API可以解决此问题。需要弄清楚如何使用它在表重命名和插入之间进行提交。