9

尝试使用 PostgreSQL 9.2 在表上运行这样的更新语句:

UPDATE table
    SET a_col = array[col];

我们需要能够在大约 10M 行的表上运行它,而不是让它锁定表(因此在更新运行时仍然可以进行正常操作)。我相信使用游标可能是正确的解决方案,但我真的不知道它是否是或者我应该如何使用游标来实现它。

我想出了这个光标代码,我认为这可能很好。

编辑:添加光标功能

创建或替换函数 update_fields() 返回无效为 $$
宣布
        游标 CURSOR FOR SELECT * FROM table ORDER BY id FOR UPDATE;
开始
        FOR 行 IN 游标 LOOP
                更新表 SET
                        a_col = 数组[col],
                        a_col2=数组[col2]
                WHERE CURRENT OF 游标;
        结束循环;
结尾;
$$ 语言 plpgsql;
4

1 回答 1

18

MVCC

首先,如果“正常操作”包含SELECT查询,MVCC 模型会自动处理它。UPDATE不会阻塞SELECT,反之亦然。SELECT只看到已提交的数据(或在同一事务中已完成的内容),因此在UPDATE完成(已提交)之前,其他事务的结果对其他事务不可见。

性能/膨胀

如果您没有其他对象引用该表,
并且您没有并发写操作(这会丢失!),
并且您可以在表上提供一个非常短的排他锁,
并且您有额外的磁盘空间,当然:
您可以通过在后台创建表的更新版本来将锁定保持在最低限度。确保它具有作为替代品的所有内容,然后删除原件并重命名副本。

CREATE TABLE tbl_new (LIKE tbl_org INCLUDING CONSTRAINTS);

INSERT INTO tbl_new 
SELECT col_a, col_b, array[col] aS col_c
FROM   tbl_org;

我正在使用CREATE TABLE (LIKE .. INCLUDING CONSTRAINTS),因为(在此处引用手册):

非空约束总是被复制到新表中。CHECK 仅当指定时才会复制约束INCLUDING CONSTRAINTS;永远不会复制其他类型的约束。

确保新表已准备就绪。然后:

DROP tbl_org;
ALTER TABLE tbl_new RENAME TO tbl_org;

导致非常短的时间窗口,其中表被独占锁定。

这实际上仅与性能有关。它会很快创建一个没有任何膨胀的新表。如果你有外键或视图,你仍然可以走这条路,但你必须准备一个脚本来删除和重新创建这些对象,这可能会创建额外的独占锁。

并发写入

使用并发写入操作,您实际上所能做的就是将您的更新分成几块。您不能在单个事务中执行此操作,因为锁定仅在事务结束时释放。

可以使用dblink,它可以在另一个数据库(包括它自己)上启动独立事务。这样,您可以在单个DO语句或带有循环的 plpgsql 函数中完成所有操作。这是一个松散相关的答案,其中包含有关 dblink 的更多信息:

您使用光标的方法

函数内的光标不会给您带来任何好处。任何功能都自动包含在事务中,所有锁仅在事务结束时释放。即使您使用CLOSE cursor(您不使用)它也只会释放一些资源,但不会释放表上获得的锁。我引用手册:

CLOSE关闭打开光标下的门户。这可用于在事务结束之前释放资源,或释放游标变量以再次打开。

您需要运行单独的事务或(ab)使用为您执行此操作的dblink

于 2013-04-02T18:04:14.803 回答