postgresql - 在 PostgreSQL 9.2 中更新数据库行而不锁定表

标签 postgresql sql-update database-cursor

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

UPDATE table
    SET a_col = array[col];

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

我想出了这个游标代码,我认为它可能不错。

编辑:添加游标功能

CREATE OR REPLACE FUNCTION update_fields() RETURNS VOID AS $$
DECLARE
        cursor CURSOR FOR SELECT * FROM table ORDER BY id FOR UPDATE;
BEGIN
        FOR row IN cursor LOOP
                UPDATE table SET
                        a_col = array[col],
                        a_col2= array[col2]
                WHERE CURRENT OF cursor;
        END LOOP;
END;
$$ LANGUAGE plpgsql;

最佳答案

MVCC

首先,如果“正常操作”包含SELECT 查询,MVCC model会自动处理它。 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),因为(quoting the manual here):

Not-null constraints are always copied to the new table. CHECK constraints will only be copied if INCLUDING CONSTRAINTS is specified; other types of constraints will never be copied.

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

DROP tbl_org;
ALTER TABLE tbl_new RENAME TO tbl_org;

结果在一个非常短的时间窗口内,表被独占锁定。

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

并发写入

对于并发写入操作,实际上您所能做的就是将更新分成 block 。您不能在单个事务中执行此操作,因为锁只会在事务结束时释放。

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

你的游标方法

函数内的光标不会给你买任何东西。。任何函数都自动包含在一个事务中,所有的锁只在事务结束时释放。 即使你使用了 CLOSE cursor (你不这样做)它只会释放一些资源,但不会释放表上获得的锁。我引用手册:

CLOSE closes the portal underlying an open cursor. This can be used to release resources earlier than end of transaction, or to free up the cursor variable to be opened again.

您需要运行单独的事务或(ab)使用dblink这是为你做的。

关于postgresql - 在 PostgreSQL 9.2 中更新数据库行而不锁定表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15770734/

相关文章:

c# - 在 ASP.NET Core 中为每个 SQL 命令打开/关闭数据库

sql - Postgresql 检查字符串是否包含多个字母

django - 从查询集中的 jsonfield 中选择值

sql - 在 PostgresQL 中将重复值设置为 Null 返回值之一

Postgresql时间戳和NULL区别

mysql - 当当前日期超过表中的特定日期时,Laravel 自动更新表中的字段

sql - 当我使用 CASE 将字段设置为 NULL 时,为什么 PostgreSQL 会提示类型?

sql - 在 SQL Server 2008 R2 中使用游标更新

mysql - 避免在 Python 中使用游标

node.js - Node Mongo Native - 如何判断游标何时耗尽?