我有一个类似下面的查询,以便在每次交易通过特定网关发生后增加交易次数。以下查询用于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
)。如何确保多个线程不会同时尝试更新计数并最终导致错误数据?
答案 0 :(得分:0)
statvalue
的更新不会丢失,因为UPDATE
是原子的。该行将在语句之前被锁定,并且只有在事务提交时才会释放锁。
但是,虽然您不能丢失statvalue
的更新,但其他问题可能会发生:
并发交易可以更新或删除SELECT
和UPDATE
之间的行。
并发交易可能会在SELECT
和INSERT
之间插入有冲突的行。
使用会锁定行的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;