繁体   English   中英

从 Java 应用程序的 postgresql 中选择并锁定随机行

[英]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 秒的时间才能返回。 有没有更好的方法来做到这一点?

您的第一次选择和第一次更新存在竞争条件:

  • 想象 2 个不同的线程同时执行步骤 1,并且都碰巧访问同一行。 在这一点上is_locked = false因此两者都可以选择同一行。
  • 在第 2 步中,两个线程都将尝试更新该行。 线程 1 将立即成功,线程 2 将被阻塞,直到线程 1 的事务提交。
  • 一旦线程 1 提交,线程 2 将再次将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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM