アーウィンの提案はおそらく最も単純な 正しい動作を取得する方法(SQLSTATE
で例外が発生した場合にトランザクションを再試行する限り of 40001)、キューイングアプリケーションは、その性質上、SERIALIZABLE
のPostgreSQL実装よりも、キューで順番を変えるチャンスをブロックするリクエストでより適切に機能する傾向があります。 トランザクション。これにより、より高い同時実行性が可能になり、衝突の可能性についていくらか「楽観的」になります。
質問のクエリ例は、現状では、デフォルトのREAD COMMITTED
にあります。 トランザクション分離レベルでは、2つ(またはそれ以上)の同時接続で、両方がキューから同じ行を「要求」できます。何が起こるかはこれです:
- T1が開始し、
UPDATE
の行をロックするところまで到達します フェーズ。 - T2は実行時にT1とオーバーラップし、その行を更新しようとします。保留中の
COMMIT
をブロックします またはROLLBACK
T1の。 - T1はコミットし、行を正常に「要求」しました。
- T2は行を更新しようとし、T1がすでに持っていることを検出し、行の新しいバージョンを探し、それがまだ選択基準(
id
)を満たしていることを検出します。 一致)、および行を「要求」します。
正しく動作するように変更できます(FOR UPDATE
を許可するバージョンのPostgreSQLを使用している場合 サブクエリの句)。 FOR UPDATE
を追加するだけです IDを選択するサブクエリの最後まで、これが発生します:
- T1が起動し、選択する前に行をロックします。 ID。
- T2は、実行時間とブロックでT1とオーバーラップし、IDを選択しようとしている間、
COMMIT
を保留します。 またはROLLBACK
T1の。 - T1はコミットし、行を正常に「要求」しました。
- T2が読むことができるようになるまでに IDを表示する行は、それが要求されていることを確認するため、次に使用可能なIDを検索します。
REPEATABLE READ
で またはSERIALIZABLE
トランザクション分離レベルでは、書き込みの競合によりエラーがスローされます。これをキャッチして、SQLSTATEに基づくシリアル化の失敗であると判断し、再試行してください。
一般にSERIALIZABLEトランザクションが必要であるが、キューイング領域での再試行を回避したい場合は、アドバイザリロックを使用してそれを実行できる可能性があります。