web-dev-qa-db-ja.com

Postgresでの同時更新の最適化

私はこのようなPostgresクエリを同時に実行しています:

UPDATE foo SET bar = bar + 1 WHERE baz = 1234

各クエリは固定されたK行数に影響し、行が更新される順序を強制する方法が見つからないため、デッドロックが発生します。現在、私は手動で順序を強制することで問題を解決していますが、これは、通常よりも多くのクエリを実行しなければならず、検索の複雑さをO(log N + K)からO(K log N)に上げる必要があることを意味します。

デッドロックに脆弱になることなくパフォーマンスを向上させる方法はありますか? (baz)インデックスと(baz, id) Postgresが行をスキャンしたのと同じ順序で行を更新する場合、インデックスは機能する可能性がありますが、これは追求する価値のあるアプローチですか?

9

ORDER BYコマンドにはSQL UPDATEはありません。 Postgresは任意の順序で行を更新します。

確実なデッドロックを回避するには、ステートメントを serializable transaction isolation で実行します。しかし、それはより費用がかかり、シリアル化の失敗時にコマンドを繰り返す準備をする必要があります。

最善の方法は、サブクエリまたはスタンドアロンのSELECTSELECT ... 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制約を考慮する を検討してください。関連する回答:

15