あなたが言及している関連する答えで:
- PostgresUPDATE...制限1
目的は、1つをロックすることです 一度に行。 デッドロックの可能性がないため、これはアドバイザリロックの有無にかかわらず正常に機能します。 -同じトランザクションでより多くの行をロックしようとしない限り。
あなたの例は、一度に3000行をロックしたいという点で異なります 。 あります すべての同時書き込み操作が同じ一貫した順序で行をロックする場合を除いて、デッドロックの可能性があります。ドキュメントごと:
デッドロックに対する最善の防御策は、通常、データベースを使用するすべてのアプリケーションが一貫した順序で複数のオブジェクトのロックを取得することを確認することにより、デッドロックを回避することです。
サブクエリでORDERBYを使用して実装します。
UPDATE cargo_item item
SET job_id = 'SOME_UUID', job_ts = now()
FROM (
SELECT id
FROM cargo_item
WHERE state='NEW' AND job_id is null
ORDER BY id
LIMIT 3000
FOR UPDATE
) sub
WHERE item.id = sub.id;
すべてである限り、これは安全で信頼できます トランザクションは同じ順序でロックを取得し、順序列の同時更新は予期されません。 (マニュアルのこの章の最後にある黄色の「注意」ボックスをお読みください。)したがって、id
を更新する予定はないので、これはあなたの場合は安全です。 列。
事実上、一度に1つのクライアントのみがこの方法で行を操作できます。同時トランザクションは、同じ(ロックされた)行をロックしようとし、最初のトランザクションが終了するのを待ちます。
アドバイザリーロック 実行中の同時トランザクションが多数または非常に長い場合に役立ちます(そうではないようです)。ほんの数個で、上記のクエリを使用して同時トランザクションを順番に待機させる方が全体的に安価になります。
オールインワンアップデート
セットアップでは、同時アクセス自体は問題ではないようです。同時実行性は、現在のソリューションによって作成された問題です。
代わりに、すべてを1つのUPDATE
で実行してください 。 n
のバッチを割り当てます 各UUIDに番号(例では3000)を付けて、一度に更新します。最速である必要があります。
UPDATE cargo_item c
SET job_id = u.uuid_col
, job_ts = now()
FROM (
SELECT row_number() OVER () AS rn, uuid_col
FROM uuid_tbl WHERE <some_criteria> -- or see below
) u
JOIN (
SELECT (row_number() OVER () / 3000) + 1 AS rn, item.id
FROM cargo_item
WHERE state = 'NEW' AND job_id IS NULL
FOR UPDATE -- just to be sure
) c2 USING (rn)
WHERE c2.item_id = c.item_id;
主なポイント
-
整数除算は切り捨てられます。最初の3000行に1、次の3000行に2を取得します。など
-
行を任意に選択します。
ORDER BY
を適用できますrow_number()
のウィンドウ内 特定の行を割り当てる。 -
ディスパッチするUUIDのテーブルがない場合(
uuid_tbl
)、VALUES
を使用します それらを供給するための表現。例。 -
3000行のバッチを取得します。割り当てる3000の倍数が見つからない場合、最後のバッチは3000未満になります。