User_id 2に500000レコードがある大きなテーブルテストがあります。だから私はこのレコードを100レコードのチャンクで削除したいのですが、エラーが出ます。これが私のクエリです:
delete from test where test_id in (select test_id
from test where User_id = 2 limit 100 )
エラー:タプルが同時に更新されました
どうした。どうすれば解決できますか?.
ようやく解決策が得られました。テーブルのインデックスを再作成しているだけです。
reindex table test
皆様、ご提案ありがとうございます。
単純な副選択は最大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の現在のバージョンへのアップグレードを検討してください。
あなたは試してみたいかもしれません:
削除の候補行を削除から分離する
例えば:
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;
{終了ループ}
0.5M行を削除しますか?
ユーザー2のデータが占めるテーブルの割合はどのくらいですか?
分数を超える場合は、このデータなしでテーブルを再作成することを検討してください。