Postgresでの同時更新の最適化


9

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

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

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

デッドロックに脆弱になることなくパフォーマンスを向上させる方法はありますか?Postgresが行をスキャンしたのと同じ順序で行を更新すれば(baz)(baz, id)索引を索引に置き換えるとうまくいくと思いますが、これは追求する価値のあるアプローチですか?


CREATE TABLEコードを追加することをお勧めします。
ypercubeᵀᴹ

回答:


15

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

確実なデッドロックを回避するために、シリアライズ可能なトランザクション分離でステートメントを実行できます。しかし、それはより高価であり、シリアル化の失敗時にコマンドを繰り返す準備をする必要があります。

最善の方法は、トランザクションのSELECT ... ORDER BY ... FOR UPDATEサブクエリまたはスタンドアロンSELECTで明示的にロックすることです-デフォルトの「コミットされた読み取り」分離レベルで。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更新を複数列インデックスなしの可能性は?...

bazが同時に更新される場合でも、(ドキュメントごとに)競合が発生する可能性はほとんどありません。

ことが可能であるSELECTコマンドで動作するREAD COMMITTED トランザクション分離レベルと使用ORDER BYとロック句は順不同の行を返すこと。...

また、あなたが関与するユニーク制約を持つべきであるならばbar検討するDEFERRABLE制約を同じコマンド内で一意の違反を避けるために。関連する回答:


1
id代わりにbar、または他のユニークな列で注文している場合、コーナーケースやパフォーマンスへの影響はありませんか?
Alexei Averchenko 2014年

@AlexeiAverchenko:はい、更新されない一意の列はこれに最適です-この列を2番目の位置に含む複数列のインデックス。
Erwin Brandstetter 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.