防止在Postgresql数据库行上进行并发更新

时间:2018-03-23 10:48:33

标签: php postgresql concurrency

我有一个类似下面的查询,以便在每次交易通过特定网关发生后增加交易次数。以下查询用于PHP应用程序,该应用程序执行事务维护作业。如何确保两个并发更新不会在计数中造成麻烦?

考虑使用PostgreSQL的咨询锁,但不确定如何在下面的上下文中使用它。在Java中很少引用,但在PHP中相对较新。

查询1(查找要更新的记录):

SELECT id
FROM Client.gatewaystat_tbl
WHERE clientid = 1 AND gatewayid = 2 ;

如果找到记录:

UPDATE gatewaystat_tbl gt
SET statvalue = statvalue + 1
WHERE id = '<from above query>'

如果找不到记录:

INSERT INTO client.gatewaystat_tbl
   (gatewayid, clientid, statetypeid, statvalue)
VALUES (2, 1, 1, 1);

上述代码可能会在一秒钟内被多次调用,因为交易将在高峰时段以非常高的交易量发生,并且在每次交易之后需要增加计数器(statvalue)。如何确保多个线程不会同时尝试更新计数并最终导致错误数据?

1 个答案:

答案 0 :(得分:0)

statvalue的更新不会丢失,因为UPDATE是原子的。该行将在语句之前被锁定,并且只有在事务提交时才会释放锁。

但是,虽然您不能丢失statvalue的更新,但其他问题可能会发生:

  1. 并发交易可以更新或删除SELECTUPDATE之间的行。

  2. 并发交易可能会在SELECTINSERT之间插入有冲突的行。

  3. 使用会锁定行的SELECT ... FOR UPDATE可以保护自己免受第一个问题的困扰,但您必须准备好在INSERT期间处理错误。

    如果(gatewayid, clientid)上有主键或唯一约束,则可以在一个语句中完成所有操作并避免所有这些问题:

    INSERT INTO client.gatewaystat_tbl
       (gatewayid, clientid, statetypeid, statvalue)
    VALUES (2, 1, 1, 1)
    ON CONFLICT (gatewayid, clientid) DO UPDATE
       SET statvalue = statvalue + 1;