Rob*_*ler 15 postgresql concurrency locking update
我有两张桌子。一个是日志表;另一个本质上包含只能使用一次的优惠券代码。
用户需要能够兑换优惠券,这将在日志表中插入一行并将优惠券标记为已使用(通过将used
列更新为true
)。
当然,这里存在明显的竞争条件/安全问题。
我过去在 mySQL 的世界里做过类似的事情。在那个世界中,我会全局锁定两个表,在知道这一次只能发生一次的情况下执行逻辑安全,然后在我完成后解锁表。
Postgres 有没有更好的方法来做到这一点?特别是,我担心锁是全局的,但不是必须的——我真的只需要确保没有其他人试图输入那个特定的代码,所以也许一些行级锁会起作用?
Erw*_*ter 21
我以前听说过 MySQL 中类似的并发问题。在 Postgres 中不是这样。
默认READ COMMITTED
事务隔离级别中的内置行级锁就足够了。
我建议使用带有数据修改 CTE 的单个语句(MySQL 也没有),因为将值从一个表直接传递到另一个表很方便(如果您需要的话)。如果您不需要coupon
表中的任何内容,您也可以使用带有单独UPDATE
和INSERT
语句的事务。
WITH upd AS (
UPDATE coupon
SET used = true
WHERE coupon_id = 123
AND NOT used
RETURNING coupon_id, other_column
)
INSERT INTO log (coupon_id, other_column)
SELECT coupon_id, other_column FROM upd;
Run Code Online (Sandbox Code Playgroud)
多次交易试图兑换同一张优惠券应该是一件罕见的事情。他们有一个唯一的编号,不是吗?然而,同时尝试多个事务的情况应该少得多。(也许是应用程序错误或有人试图玩弄系统?)
尽管如此,无论如何UPDATE
,只有一次交易才能成功。An在更新之前UPDATE
获取每个目标行上的行级锁。如果并发事务尝试访问UPDATE
同一行,它将看到该行上的锁并等待阻塞事务完成(ROLLBACK
或COMMIT
),然后成为锁队列中的第一个:
如果提交,请重新检查条件。如果它仍然是NOT used
,请锁定该行并继续。否则UPDATE
now 找不到符合条件的行并且什么都不做,也不返回任何行,所以INSERT
也什么都不做。
如果回滚,请锁定该行并继续。
有一个竞争条件没有潜力。
有一个没有潜在的死锁,除非你把更多的写入同一交易或其他方式锁定不仅仅是多了一个行。
该INSERT
是无忧无虑。如果由于某些错误coupon_id
已经在log
表中(并且您在 上有 UNIQUE 或 PK 约束log.coupon_id
),则整个事务将在唯一违规后回滚。将指示您的数据库中的非法状态。如果上述语句是写入log
表的唯一方法,则永远不会发生这种情况。
归档时间: |
|
查看次数: |
19787 次 |
最近记录: |