【问题标题】:Locking a specific row in postgres锁定 postgres 中的特定行
【发布时间】:2018-12-02 19:23:57
【问题描述】:

我对 Postgres 还很陌生,我正在尝试弄清楚如何锁定表格的特定行。

例如,我有一个用户表:

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

在我的后端,我想选择 John 并确保在我的事务完成之前没有其他调用可以更新(甚至可能选择)John 的行。

我认为我需要一个我在网上阅读的内容的排他锁?我似乎找不到一个很好的例子,可以在线锁定表格中的 1 行,知道吗?

编辑 - 我应该在像 @SqlUpdate 这样的方法级别(或某种形式 - 使用 org.skife.jdbi.v2)还是在查询本身中这样做?

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    一旦您更新(而不是提交)该行,其他事务将无法更新该行。

    如果你想在更新之前锁定行(这似乎没用),你可以使用select ... for update

    您不能阻止其他会话读取该行,坦率地说,这也没有任何意义。

    即使您的事务尚未完成(=已提交),其他会话也将不会看到任何中间(不一致)值 - 他们将看到数据库的状态,就像您的事务开始之前一样。这就是拥有支持事务的关系数据库的全部意义所在。

    【讨论】:

    • 谢谢。不知道它是如何无用的。在我的后端,我想更新“金钱”的价值。所以我的想法是:用锁选择,做一些检查,为“Money”添加一个值,然后最后更新它。当我还在做一些检查时,我不能让另一个电话看到“钱”的价值,真的可以吗?
    • @Greg:为什么你认为你不能让其他交易看到“旧”的价值?他们将无法更新行,也不会看到数据库的不一致状态。
    • @a_horse_with_no_name 当您有很多异步事务并且该异步方法执行选择时,选择一些数据以在短时间内进行验证和更新,因为其他事务可能是旧值。我不再使用我回答的锁,在我的模型中不再有意义,但这是可以使用的示例。但是,是的,您不应该选择将要更改的值,而是选择逻辑字段并更改目标字段
    • 有一些用例,在我的例子中,我有一个服务器,客户端在其中查询下一个可用的工作单元。然后我更新工作单元行以表示分配给它的客户端。我不想将同一个工作单元分配给多个用户,所以我select ... for update 来避免这种竞争条件。
    【解决方案2】:

    如果您想将表锁定在特定的选定行中,您需要LOCK FIRST 他们使用FOR UPDATE / FOR SHARE 语句。 例如,在您的情况下,如果您需要锁定第一行,请执行以下操作:

    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;
    

    BLOCK1 之前的SELECT 语句中,您什么都不做,只是告诉数据库“嘿,我会在这个表中做一些事情,所以当我这样做时,将这个表锁定在这种模式下”。您可以选择/更新/删除任何行。

    但是在BLOCK2 中,当您使用FOR UPDATE 时,您将该行锁定到其他事务到特定模式(阅读doc 了解更多详细信息)。将被锁定直到该事务结束。

    如果您需要一个示例,请在结束第一个事务之前尝试在BLOCK2 中执行另一个SELECT ... FOR UPDATE。它将等待第一个事务结束并在它之后立即选择。

    只有一个 ACCESS EXCLUSIVE 锁会阻塞一个 SELECT(没有 FOR UPDATE/SHARE) 语句。

    我在一个函数中使用它来控制子序列,它很棒。希望你喜欢。

    【讨论】:

    • 这不起作用,排除模式不会阻止普通选择,请参阅postgresql.org/docs/9.4/explicit-locking.html。我认为没有办法禁止纯选择。
    • "在结束第一个事务之前。它将等待第一个事务结束并在它之后立即选择。"我确定,试试start transaction;do the lock;打开其他控制台,再次开始事务并在该行使用select ... for update,它会等待
    • 我试过了,不管你在表上设置了什么锁,也不管你是否使用 FOR UPDATE/SHARE,这些都不会阻止普通的选择查询。看一下表格,更新/共享选择如何相互锁定,没有什么关于普通选择的。而且我认为这是非常合乎逻辑的,添加一个将锁定所有其他选择的选择,无论它们是否用于某事听起来都是一种不好的做法。
    • 在您发送的链接中有一个灰色块,准确地说明了此处突出显示的内容:“提示:只有 ACCESS EXCLUSIVE 锁会阻止 SELECT(没有 FOR UPDATE/SHARE)语句。”下面是我在解释中所说的“一旦获得,通常会保持锁定直到事务结束。”...
    • 选择在没有LOCK TABLE person IN ROW EXCLUSIVE MODE;的postgres 12中更新作品
    【解决方案3】:

    你可以使用

    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 情况考虑的列集是那些具有唯一索引的列,可以在外键中使用(因此不考虑部分索引和表达式索引),但这可能会在未来发生变化。*

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-14
      • 1970-01-01
      • 2015-08-07
      • 2023-03-16
      相关资源
      最近更新 更多