私はこのようなPostgresクエリを同時に実行しています:
UPDATE foo SET bar = bar + 1 WHERE baz = 1234
各クエリは固定されたK行数に影響し、行が更新される順序を強制する方法が見つからないため、デッドロックが発生します。現在、私は手動で順序を強制することで問題を解決していますが、これは、通常よりも多くのクエリを実行しなければならず、検索の複雑さをO(log N + K)からO(K log N)に上げる必要があることを意味します。
デッドロックに脆弱になることなくパフォーマンスを向上させる方法はありますか? (baz)
インデックスと(baz, id)
Postgresが行をスキャンしたのと同じ順序で行を更新する場合、インデックスは機能する可能性がありますが、これは追求する価値のあるアプローチですか?
ORDER BY
コマンドにはSQL UPDATE
はありません。 Postgresは任意の順序で行を更新します。
確実なデッドロックを回避するには、ステートメントを serializable transaction isolation で実行します。しかし、それはより費用がかかり、シリアル化の失敗時にコマンドを繰り返す準備をする必要があります。
最善の方法は、サブクエリまたはスタンドアロンのSELECT
で SELECT ... ORDER BY ... FOR UPDATE
を明示的にロックすることです。トランザクション-デフォルトでは「コミットされた読み取り」分離レベル。 pgsql-generalでTom Laneを引用 :
大丈夫です--- FOR UPDATEロックは常にSELECTパイプラインの最後のステップです。
これは仕事をするはずです:
BEGIN;
SELECT 1
FROM foo
WHERE baz = 1234
ORDER BY bar
FOR UPDATE;
UPDATE foo
SET bar = bar + 1
WHERE baz = 1234;
COMMIT;
(baz, bar)
の複数列インデックスは、パフォーマンスに最適な場合があります。しかし、bar
は明らかに更新されるたくさんなので、(baz)
だけの単一列のインデックスの方が良いかもしれません。いくつかの要因に依存します。 baz
あたりの行数はいくつですか? HOT更新 は複数列インデックスなしで可能ですか? ...
Ifbaz
が同時に更新されても、可能性は低いコーナーケースの可能性があります。競合 (ドキュメントごと) :
READ COMMITTED
トランザクション分離レベルでSELECT
コマンドを実行し、ORDER BY
およびロッキング句を使用して行を順序どおりに返さないようにすることができます。 ...
また、bar
に関連する一意の制約が必要な場合は、同じコマンド内で一意の違反を回避するために DEFERRABLE
制約を考慮する を検討してください。関連する回答: