在Postgresql中锁定一个事务(一系列选择和更新)

时间:2014-01-13 17:11:30

标签: postgresql locking

我有一个表和几个线程(系统),它们从表中读取特定列并进行更新。所以我的查询如下:

Begin;
SELECT col FROM tbl WHERE <condition1>
UPDATE tbl SET col = col + 1 WHERE <condition1>
Commit;

我想确保在任何时候只有一个线程可以选择并更新该列。什么是最好的解决方案?

PS:据我所知,在SQL Server中使用事务会锁定事务中的所有资源,但我不知道PostgreSQL。

谢谢!

2 个答案:

答案 0 :(得分:2)

SQL中最好的级别锁定是行锁定。

无法锁定列,数据库始终锁定整行(行的所有列)。

您可以使用a SELECT .. FOR UPDATE clause

Begin;
SELECT col FROM tbl WHERE <condition1> FOR UPDATE;
UPDATE tbl SET col = col + 1 WHERE <condition1> ;
Commit;

SELECT .. FOR UPDATE的作用类似于普通的select(它从表中检索行),但是在附加中它会锁定所有检索到的行,防止它们被其他事务修改,直到当前事务结束。

您还可以使用RETURNING clause in UPDATE statement

Begin;
UPDATE tbl SET col = col + 1 WHERE <condition1> 
   RETURNING col - 1 AS col;
Commit;

UPDATE语句总是在修改后的记录上放置一个写锁定。

RETURNING子句导致返回值,如普通SELECT,但是它会在修改后返回行值(更新后的行值),因此在col - 1表达式中使用SELECT表达式。上面的示例,计算列值增加之前的值。

请看一下这个演示 - &gt; http://www.sqlfiddle.com/#!15/4d0f4/3

第二种方式(UPDATE + RETURNING子句)的优点是记录只访问一次,与第一种方法相反,必须首先通过UPDATE语句选择行(第一次访问) ,然后使用{{1}}语句(第二次访问)更新它。

答案 1 :(得分:1)

kordirko已经正确地指出SELECT ... FOR UPDATE是用于此的基本功能。

请注意,它不会采用谓词锁定。因此,如果您SELECT id FROM sometable WHERE id = 42 FOR UPDATE并且没有id = 42行,则不会锁定。 ID 42上没有创建“预订”。其他人可以在SELECT和后续INSERT之间跳转并创建它。

如果您正在努力解决这个问题,您可能希望研究SERIALIZABLE隔离模式。我认为您习惯使用的MS SQL Server默认为SERIALIZABLE隔离。

你可以写:

BEGIN ISOLATION LEVEL SERIALIZABLE;
SELECT col FROM tbl WHERE <condition1>
UPDATE tbl SET col = col + 1 WHERE <condition1>
COMMIT;

我强烈建议您设置一些并发测试,以证明这可以按预期工作。