如何在PostgreSQL中原子替换表数据


14

我想替换表的全部内容,而不会影响SELECT过程中的任何传入语句。

用例是拥有一个表,该表存储定期提取的邮箱信息,并且需要将其存储在PostgreSQL表中。有许多客户端正在使用不断查询同一张表的应用程序。

通常,我会做类似(伪代码传入)的操作...

BEGIN TRANSACTION
TRUNCATE TABLE
INSERT INTO
COMMIT

但不幸的是,在此过程中无法读取该表。由于需要花费时间INSERT INTO。该表已锁定。

在MySQL中,我本可以使用其原子RENAME TABLE命令来避免这些问题...

CREATE TABLE table_new LIKE table; 
INSERT INTO table_new;
RENAME TABLE table TO table_old, table_new TO table; *atomic operation*
DROP TABLE table_old;

如何在PostgreSQL中实现呢?

出于这个问题的目的,您可以假设我没有使用外键。


您为什么认为在表中插入行时无法读取表?截断表将在所有会话中立即生效;但是,插入(如果在将它们全部包裹的事务中完成,如您的伪代码所建议的那样)在您提交之前对其他会话不可见。其他会话将能够从表中选择,并且将看到一个空表,直到您提交为止。
zgguy 2015年

2
@zgguy该TRUNCATE命令将在表上获取一个AccessExclusive锁,因此在提交该事务或将其回滚之前,其他任何人都无法从该表读取。
Josh Kupershmidt,2015年

2
如果使用delete代替truncate它会比较慢,但是不会阻塞读者。您需要删除多少行?
a_horse_with_no_name 2015年

@a_horse_with_no_name通常在200-300k行之间,具有许多varchar列。的等待时间DELETE,并INSERT会太长。
克拉基2015年

Answers:


21

正确,您正在执行的TRUNCATE TABLE 命令 “ ... 在对其操作的每个表上都获得了ACCESS EXCLUSIVE锁 ”,因此在您发布的第一个SQL块中,此后尝试访问该表的所有其他客户端将被阻止,直到您INSERT完成操作为止和你COMMIT

您可以使用与特定于MySQL的代码相同的解决方法;Postgres支持大致相同的语法,并且具有类似的锁定行为。以机智:

BEGIN;
-- You probably want to make sure that no one else is
-- INSERT / UPDATE / DELETE'ing from the original table, otherwise
-- those changes may be lost during this switchover process. One way
-- to do that would be via:
-- LOCK TABLE "table" IN SHARE ROW EXCLUSIVE mode;
CREATE TABLE "table_new" (LIKE "table");
INSERT INTO "table_new" ...;

-- The ALTER TABLE ... RENAME TO command takes an Access Exclusive lock on "table",
-- but these final few statements should be fast.
ALTER TABLE "table" RENAME TO "table_old";
ALTER TABLE "table_new" RENAME TO "table";
DROP TABLE "table_old";

COMMIT;

额外的好处:Postgres实际上支持事务性DDL,这与MySQL不同,因此,如果您需要回滚上述事务,则可以放心地这样做。


我将对此做一些测试,谢谢您的回答。如果我使用了LOCK TABLE您建议的方法,是否需要在之前再次COMMIT将其解锁,还是将自身解锁?
克拉基2015年

1
编辑:在本文档中找到以下陈述:“没有UNLOCK TABLE命令;锁始终在事务结束时释放。”
克拉基2015年

2
这里遗漏的一件事是仍然存在的所有约束_old
Intellix

@Intellix您可以详细说明吗?这是否意味着每个旧表都简单地命名了约束,或者它们仅旧表有关(意味着约束已被有效删除)?
maerics

-- LOCK TABLE "table" IN ROW EXCLUSIVE mode;根据规范,表创建()之前的注释似乎不足以防止将其更新/插入到源表中。ROW EXCLUSIVE可以获取两个锁而不会发生任何冲突(请参见postgresql.org/docs/10/explicit-locking.html#LOCKING-TABLES中的表13.2 )。为了防止数据更新,您至少需要一个SHARE锁。
Pilou
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.