【问题标题】:How to avoid race condition on a sequential delete and insert in postgres如何避免在 postgres 中顺序删除和插入的竞争条件
【发布时间】:2019-04-23 03:37:46
【问题描述】:

我有四个表叫:

  1. 参与者[列:ID、姓名]
  2. Answer [Columns:Id, Question_id(问题表的外键),Option_Id(选项表的外键),Participant_Id(参与者表的外键)]。
  3. 问题 [ID,标题]
  4. 选项 [Id、title、question_id(问题表的 FK)]。

参与者、问题和选​​项记录已由管理员创建,现在,当参与者回答问题时,我们每隔几秒保存一次(参与者可以回答新问题或从已回答的问题中删除答案)。

现在在保存最新答案时,我们会删除当前参与者的现有答案并插入最新答案。

现在可能会有 2 个线程来为同一参与者保存答案,因此两者都会尝试删除答案然后插入。如果两个线程同时删除然后尝试插入它会导致竞争条件并且重复的答案将被保存。

更多细节:

  1. 当前参与者有 4 个现有答案。
  2. 线程 1 和线程 2 来保存 6 和 7 的答案记录。
  3. 主题 1:已删除答案(已删除 4 个答案)。
  4. 线程 2:删除答案(已删除 0 个答案,因为线程 1 已删除)
  5. 主题 1:插入最新的 6 个答案。
  6. 线程 2:插入最新的 7 个答案。

这里当前参与者将有 6+7 = 13 个答案,这是错误的,应该是 6 或 7。

如何通过交易或任何更好的方法来处理这个问题?我不想使用java线程锁,也不想锁定整个表。

在执行删除/插入操作时如何锁定 Answer 表的参与者 ID,以便其他事务在尝试使用相同的参与者 ID 插入/更新/删除时必须等待?寻找性能最佳的解决方案。

我正在使用 Postgres SQL 和 JPA。如果需要,我已准备好使用普通 SQL 进行删除和插入。

【问题讨论】:

    标签: postgresql concurrency transactions race-condition


    【解决方案1】:

    您可以获取参与者的显式锁定:

    BEGIN;
    SELECT FROM Participant WHERE Id = $PARTICIPANT_ID FOR NO KEY UPDATE;
    DELETE FROM Answer WHERE Participant_Id = $PARTICIPANT_ID;
    INSERT INTO Answer (Question_id, Option_Id, Participant_Id)
        VALUES ($QUESTION_ID, $OPTION_ID, $PARTICIPANT_ID);
    COMMIT;
    

    这样,任何其他尝试执行相同操作的连接都必须等到此事务完成。

    要说服自己,请尝试使用两个 psql 会话。首先,您输入:

    BEGIN;
    SELECT FROM Participant WHERE Id = $PARTICIPANT_ID FOR NO KEY UPDATE;
    

    在第二个会话中,您会注意到常规的 SELECT 仍然有效:

    SELECT * FROM Participant WHERE Id = $PARTICIPANT_ID;
    

    但是在第二个会话中获取相同的锁会挂起:

    SELECT FROM Participant WHERE Id = $PARTICIPANT_ID FOR NO KEY UPDATE;
    

    它将等到您在第一个会话中COMMITROLLBACK 处理事务,从而保证一次只有一个线程持有该锁。

    如果您希望在锁不可用的情况下尽早失败而不是等待获取锁,您可以使用NOWAIT。现在在第二个会话中尝试此操作,而第一个会话中的事务仍在进行中:

    SELECT FROM Participant WHERE Id = $PARTICIPANT_ID FOR NO KEY UPDATE NOWAIT;
    

    你会立即得到一个错误:

    错误:无法获得与“参与者”相关的行的锁定

    有关更多信息,请参阅有关 explicit locking 的 PostgreSQL 文档。

    【讨论】:

      猜你喜欢
      • 2021-12-21
      • 2019-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-01
      • 2021-05-24
      • 1970-01-01
      相关资源
      最近更新 更多