我有一个表和几个线程(系统),它们从表中读取特定列并进行更新。所以我的查询如下:
Begin;
SELECT col FROM tbl WHERE <condition1>
UPDATE tbl SET col = col + 1 WHERE <condition1>
Commit;
我想确保在任何时候只有一个线程可以选择并更新该列。什么是最好的解决方案?
PS:据我所知,在SQL Server中使用事务会锁定事务中的所有资源,但我不知道PostgreSQL。
谢谢!
答案 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;
我强烈建议您设置一些并发测试,以证明这可以按预期工作。