[英]select and lock random row from postgresql from Java application
我的情况是,我有一张大约有 500 个帐户的表。 这些帐户用于进行一些金融交易提交。 当事务开始时,我需要从该表中获取一个帐户(任何随机帐户都可以工作)然后锁定该行,以便在操作完成之前没有其他行可以访问该帐户。 然后将交易提交到外部系统,然后解锁帐户。
我已经使用以下算法实现了这一点。 所有查询都在此处的事务上下文中执行。
//Get Random account
1. select * from gas_accounts where is_locked = false order by random() limit 1
//Lock that account using the primary key
2. update gas_accounts set is_locked = true, last_updated_at = current_timestamp where public_key = :publicKey
//unlock the account
3. update gas_accounts set is_locked = false, last_updated_at = current_timestamp where public_key = :publicKey
当运行 50 个并发线程时,上述函数需要大约 50 秒的时间才能返回。 有没有更好的方法来做到这一点?
您的第一次选择和第一次更新存在竞争条件:
is_locked = false
因此两者都可以选择同一行。is_locked = true
设置为同一行,并将再次处理同一个帐户。 相反,Postgres 有一个名为FOR UPDATE SKIP LOCKED
的子句,它允许您像这样执行第 1 步(第 2 步和第 3 步保持不变):
SELECT * FROM gas_accounts WHERE is_locked = false LIMIT 1 FOR UPDATE SKIP LOCKED
请注意,您甚至不需要order by random()
的order by random()
因为这将选择下一个未锁定的可用行(通过is_locked
列或 Postgres 自己的锁定),因此您可能认为这是一个随机行。
这个子句的作用是给每个事务一个当前没有被任何其他事务锁定的行,这样竞争条件就完全消失了。 只需确保您在同一事务中执行 SELECT 和第一个 UPDATE。
补充说明:
另外,请注意,如果 3 个语句发生在同一个事务中,您甚至不需要is_locked
列。 仅通过使用FOR UPDATE SKIP LOCKED
该行将保持锁定状态,并且对也使用该子句的其他事务不可见。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.