【问题标题】:Database how to lock a table ONLY for specific INSERT with specific values?数据库如何仅针对具有特定值的特定 INSERT 锁定表?
【发布时间】:2016-07-30 12:26:02
【问题描述】:

有没有办法锁定一个表,指定只有具有特定值的插入应该被阻止?
例如,我有表“任务”和表“子任务”,我需要一个操作,当任务关闭时,与其相关且仍处于打开状态的子任务也应该关闭。所以我想:

  1. 开始交易
  2. 锁定表子任务但只阻止执行具有给定任务 ID 的插入
  3. 关闭所有子任务
  4. 使用乐观锁关闭任务(如果任务版本发生变化,则全部回滚)
  5. 提交事务

是否可以按照我在第 2 步中描述的操作?如果没有(或者如果有更好的方法),我怎样才能在这种情况下获得安全的并发?

【问题讨论】:

    标签: sql database postgresql concurrency locking


    【解决方案1】:

    最好的方法是自己创建行!但是您可以在交易结束时将其删除。事情是这样的

    CREATE OR REPLACE FUNCTION ....
    AS $$
    
        BEGIN
    
           INSERT INTO subtasks VALUE(pk, ...) ON CONFLICT (id) DO NOTHING;
           GET DIAGNOSTICS inserted = ROW_COUNT
    
           if inserted THEN
               DELETE from subtasks where pk = id
           endif;
        COMMIT;
    $$ LANGUAGE plpgsql;
    

    请注意,在上面的代码中INSERT ON CONFLICT 实际上并没有对现有数据进行任何更改。它将返回更改的行数。如果没有更改行,则插入的变量将保持为零。

    这里发生的情况是,在您的事务中插入了一条记录后,任何其他连接都将无法保存相同的记录。您可以通过使用 psql 打开与数据库的连接来确认这一点。然后做

    begin;
       insert ...
    
       # just wait here
    commit;
    

    在另一个中,尝试相同的插入,您会看到它挂起。插入是成功还是失败,取决于你在第一个 psql 连接中是提交还是回滚。

    【讨论】:

    • 您好,感谢您的回答,但我不确定它是否适用于我的情况(但也许我误解了它)。为什么它应该阻止向给定任务添加子任务?
    • 不应在非唯一列上使用 ON CONFLICT。在这种情况下,列 subtask(taskId) 不是唯一的,因为许多子任务可以具有与父任务相同的任务。否则在子任务的 pk 列上使用 ON CONFLICT (即 subtask(subtaskId) )应该不起作用,因为第二个线程可以插入具有不同 id 的子任务,尊重第一个子任务插入的子任务的 id,因此不会发生冲突
    • 好吧,根据您最初提供的信息,这个答案是有效的。没有提到您没有唯一索引的事实。因此,请您将答案标记为正确并发布一个包含完整信息的新答案
    【解决方案2】:

    您无法锁定特定值的表,但在您的情况下,此解决方案将适用:在子任务中插入时创建触发器,并在此触发器中检查对应任务是否已关闭。如果任务关闭,则不允许插入。

    任务的另一个触发器将在任务关闭后立即关闭所有对应的子任务。

    【讨论】:

    • 不幸的是,此解决方案不能应用于我的情况,因为如果在线程 A 关闭所有子任务但尚未关闭主任务时发生插入(线程 B),则会导致不一致。顺便说一句,如果不可能,这里唯一的解决方案似乎是锁定整个子任务表。 Tnx
    • 我不认为应该存在这个问题,因为 postgres 函数是单个事务。任务触发器将作为单个事务运行,您不能关闭任务并打开对应的子任务。
    猜你喜欢
    • 2023-02-21
    • 2010-12-12
    • 2018-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-18
    • 2021-05-14
    • 1970-01-01
    相关资源
    最近更新 更多