私は一種のキューイングメカニズムを構築しています。処理が必要なデータ行とステータスフラグがあります。それを管理するためにupdate .. returning
句を使用しています:
UPDATE stuff
SET computed = 'working'
WHERE id = (SELECT id from STUFF WHERE computed IS NULL LIMIT 1)
RETURNING *
ネストされた選択部分は更新と同じロックですか、またはここで競合状態がありますか?その場合、内部選択はselect for update
である必要がありますか?
アーウィンの提案はおそらく正しい動作を得るための最も簡単な方法です(SQLSTATE
で例外が発生した場合にトランザクションを再試行する限り) 40001)の性質により、キューイングアプリケーションは、PostgreSQLのSERIALIZABLE
トランザクションの実装よりも、リクエストをブロックしてキューで順番をとるチャンスよりもうまく機能する傾向があります。 「衝突の可能性について。
質問の例のクエリは、現状ではデフォルトのREAD COMMITTED
トランザクション分離レベルで、2つ(またはそれ以上)の同時接続が両方ともキューから同じ行を「要求」することを許可します。これは何が起こるでしょう:
UPDATE
フェーズで行をロックするまで到達します。COMMIT
またはROLLBACK
の保留をブロックします。id
が一致するだけ)をまだ満たしていることを見つけ、さらに行。正しく動作するように変更できます(サブクエリでFOR UPDATE
句を許可するバージョンのPostgreSQLを使用している場合)。 idを選択するサブクエリの最後にFOR UPDATE
を追加するだけで、次のようになります。
COMMIT
またはROLLBACK
を保留して、IDを選択しようとしている間、実行時間でT1とオーバーラップしてブロックします。REPEATABLE READ
またはSERIALIZABLE
トランザクション分離レベルでは、書き込みの競合によりエラーがスローされ、SQLSTATEに基づいてシリアル化の失敗をキャッチして判別し、再試行できます。
一般にSERIALIZABLEトランザクションが必要であるが、キューイング領域での再試行を避けたい場合は、 アドバイザリロック を使用してそれを実現できる場合があります。