JavaアプリケーションからのPostgreSQLからランダムに行を選択し、ロック

Mayuran:

私は約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秒かかります。これを行うに任意のより良い方法はありますか?

MondKin:

あなたの最初の選択と最初のアップデートとの競合状態があります:

  • 2つの異なるスレッドが同時にステップ1を実行し、両方が同じ行にアクセスするために起こる想像します。その時点でis_locked = false、両方が同じ行を選択することが可能ですので。
  • ステップ2において、両方のスレッドは、その行を更新しようとします。スレッド1はスレッド1のトランザクションがコミットされるまで、スレッド2はブロックされます、すぐに成功します。
  • スレッド1つのコミット後、スレッド2が再び設定されますis_locked = true、同じ行にして、再度同じアカウントを処理します。
  • これは望ましいことではない、あなたは待たなければならなかったのスレッドを持っているだけでなく、あなたが二度同じアカウントを処理しました。

代わりに、Postgresはと呼ばれる条項があるFOR UPDATE SKIP LOCKED(ステップ2と3は変わらない)あなたはこのようにステップ1を行うことができるようになります:

SELECT * FROM gas_accounts WHERE is_locked = false LIMIT 1 FOR UPDATE SKIP LOCKED

通知は、あなたも必要ありませんorder by random()(あなたのいずれかによってロックされていない使用可能な次の行を選択します。このようにis_lockedあなたはそれがランダム行だ検討することができるので、Postgresの自身のロック列もでは)。

何この句がやっていることは、レースの条件が完全に消えるので、各トランザクションには現在、他のトランザクションによってロックされていない1行を与えることです。ちょうどあなたがSELECTと同じトランザクション内の最初のUPDATEを実行していることを確認します。

追加の注記:

また、3文は同じトランザクションで起こる場合、あなたも持っている必要はありませんという通知is_lockedコラムを。ただ、使用してFOR UPDATE SKIP LOCKED行をロックしてもその句を使用する他のトランザクションには見えないままになります。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=352308&siteId=1