postgresql 锁定postgres中的特定行

vbkedwbf  于 2023-01-12  发布在  PostgreSQL
关注(0)|答案(4)|浏览(260)

我对Postgres还很陌生,我正在尝试弄清楚如何锁定表的特定行。
例如,我有一个用户表:

Name: John, Money: 1
Name: Jack, Money: 2

在后端,我希望选择John,并确保在事务完成之前,没有其他调用可以更新(甚至可能选择)John的行。
我想我需要一个排它锁,从我在网上读到的,我似乎找不到一个很好的例子,锁定一个表的一行在线,你知道吗?
编辑-我应该在方法级别上进行编辑,比如@SqlUpdate(或者其他形式-使用org.skife.jdbi.v2),还是在查询本身中进行编辑?

tzcvj98z

tzcvj98z1#

如果要锁定表中选定的特定行,则需要使用FOR UPDATE / FOR SHARE语句对它们执行LOCK FIRST操作。例如,在您的示例中,如果需要锁定第一行,则执行以下操作:

BEGIN;

LOCK TABLE person IN ROW EXCLUSIVE MODE;

-- BLOCK 1

SELECT * FROM person WHERE name = 'John' and money = 1 FOR UPDATE;

-- BLOCK 2

UPDATE person set name = 'John 2' WHERE name = 'John' and money = 1;

END;

SELECT语句之前的BLOCK1中,您什么也没做,只是告诉数据库“嘿,我将在此表中执行某些操作,因此当我执行这些操作时,请在此模式下锁定此表”。
但是在BLOCK2中,当您使用FOR UPDATE时,您将该行锁定到特定模式的其他事务(阅读doc了解更多详细信息)。将被锁定,直到该事务结束。
如果你需要一个例子,做一个测试,并尝试在BLOCK2中做另一个SELECT ... FOR UPDATE,然后再结束第一个事务。它将等待第一个事务结束,并在它之后选择。
只有ACCESS EXCLUSIVE锁会阻止SELECT(不带FOR UPDATE/SHARE)语句。
我正在一个函数中使用它来控制子序列,它很棒。希望你喜欢。

6yt4nkrj

6yt4nkrj2#

一旦更新(而不是提交)该行,其他事务就无法更新该行。
如果您想在执行更新之前 * 锁定该行(这似乎没有用),可以使用select ... for update来实现。
您不能阻止其他会话阅读该行,坦率地说,这也没有意义。
即使你的事务还没有完成(=提交),其他会话也不会看到任何中间(不一致)值--它们看到的是事务开始之前的数据库状态,这就是拥有一个支持事务的关系数据库的全部意义。

bvn4nwqk

bvn4nwqk3#

您可以使用

LOCK TABLE table IN ACCESS EXCLUSIVE MODE;

当您准备好从表中读取时,."SELECT"和所有其他操作将排队,直到事务结束(提交更改或回滚)。
请注意,这将锁定整个表,并且在PostgreSQL中,没有表级锁可以专门锁定特定的行。
所以你可以用

FOR UPDATE

行级别锁定所有将更新行SELECT,这将阻止所有将更新行的SELECT读取您的行!

    • PostgreSQL文档:**
      *FOR UPDATE**导致SELECT语句检索到的行被锁定,就像要进行更新一样。这可以防止在当前事务结束之前,其他事务锁定、修改或删除这些行。即,尝试UPDATE、DELETE、SELECT FOR UPDATE、SELECT FOR NO KEY UPDATE、这些行的SELECT FOR SHARE或SELECT FOR KEY SHARE将被阻塞,直到当前事务结束;相反,SELECT FOR UPDATE将等待在同一行上运行了这些命令中的任何一个的并发事务,然后锁定并返回更新的行(如果行被删除,则不返回任何行)。但是,在REPEATABLE READ或SERIALIZABLE事务中,如果要锁定的行在事务开始后发生了更改,则会抛出错误。有关详细讨论,请参阅13.4节。

FOR UPDATE锁模式也可以由行上的任何DELETE获取,也可以由修改某些列上的值的UPDATE获取。目前,UPDATE情况下考虑的列集是那些具有唯一索引的列,可以在外键中使用(因此不考虑部分索引和表达式索引),但这在将来可能会改变。*

gt0wga4j

gt0wga4j4#

我正在使用我自己的表,我的表名是paid_properties,它有两列user_idcounter
由于您希望一次只处理一个事务,因此可以使用以下锁之一:

      • FOR UPDATE**模式假定对一行进行全部更改(或删除)。
      • FOR NO KEY UPDATE**模式假定只对不涉及唯一索引的字段进行更改(换句话说,此更改不影响外键)。
    • UPDATE命令本身选择最小的适当锁定模式;行通常在FOR NO KEY UPDATE**模式下被锁定。

要测试它,在一个选项卡中运行以下查询(我使用pgadmin4):

BEGIN;
SELECT * FROM paid_properties WHERE user_id = 37 LIMIT 1 FOR NO KEY UPDATE;
SELECT pg_sleep(60);
UPDATE paid_properties set counter = 4 where user_id = 37;
-- ROLLBACK; -- If you want to discard the operations you did above
END;

并在另一个选项卡中显示以下查询:

UPDATE paid_properties set counter = counter + 90 where user_id = 37;

您将看到第二个查询在第一个查询完成之前不会执行,您将得到答案94,这在我的情况下是正确的。
如需了解更多信息:https://postgrespro.com/blog/pgsql/5968005https://www.postgresql.org/docs/current/explicit-locking.html
希望这是有帮助的

相关问题