web-dev-qa-db-ja.com

Postgresエラー:タプルが同時に更新されました

User_id 2に500000レコードがある大きなテーブルテストがあります。だから私はこのレコードを100レコードのチャンクで削除したいのですが、エラーが出ます。これが私のクエリです:

delete from test where test_id in (select test_id
from test where User_id = 2 limit 100 )

エラー:タプルが同時に更新されました

どうした。どうすれば解決できますか?.

5
Saddam Khan

ようやく解決策が得られました。テーブルのインデックスを再作成しているだけです。

reindex table test

皆様、ご提案ありがとうございます。

3
Saddam Khan

単純な副選択は最大100行をフェッチしますが、書き込みアクセスに対してそれらをロックしません。並行トランザクションは、DELETEが行をロックする前に、それらの行の1つ以上を更新または削除できます(少なくとも デフォルトの分離レベルREAD COMMITTED を使用)。これにより、エラーメッセージが表示されます。

この競合状態を防ぐには、FOR UPDATE(またはその他のオプション)を使用してSELECTの行をロックします。

DELETE FROM test t
USING (
   SELECT test_id
   FROM   test
   WHERE  User_id = 2
   ORDER  BY test_id  -- acquire locks in consistent order!
   LIMIT  100
   FOR UPDATE         -- lock rows
   ) x
WHERE x.test_id = t.test_id;

これを一貫した順序で実行して、複数のトランザクション間でデッドロックが発生し、ロックが異なる順序で収集され、最終的に互いにブロックされることを防ぎます。

さらに良い解決策があります。 Postgres 9.5以降では、FOR UPDATE SKIP LOCKEDを使用するのが最良のオプションです。

Postgresの現在のバージョンへのアップグレードを検討してください。

5

あなたは試してみたいかもしれません:

  1. 削除の候補行を削除から分離する
    例えば:
    TEMPテーブルを作成しますtmp_test1 AS SELECT test_id FROM test WHERE user_id = 2;
    一時テーブルを作成tmp_test2 AS SELECT * FROM tmp_test1 LIMIT 100;
    {tmp_test2に行がある間ループします}
    DELETE FROMテストWHERE test_id IN(SELECT test_id FROM tmp_test2);
    DELETE FROM tmp_test1 WHERE test_id IN(SELECT test_id FROM tmp_test2);
    切り捨てテーブルtmp_test2;
    INSERT INTO tmp_test2 SELECT * FROM tmp_test1 LIMIT 100;
    {終了ループ}

  2. 0.5M行を削除しますか?
    ユーザー2のデータが占めるテーブルの割合はどのくらいですか?
    分数を超える場合は、このデータなしでテーブルを再作成することを検討してください。

2
bentaly