これは最近、SOとPostgreSQLメーリングリストの両方で数回発生しています。
TL; DR 最後の2つのポイント:
(a)shared_buffersが大きいことが、CIサーバーでTRUNCATEが遅い理由である可能性があります。異なるfsync構成またはSSDの代わりに回転メディアを使用することも原因である可能性があります。
(b)TRUNCATE
固定費がかかりますが、必ずしもDELETE
より遅いとは限りません。 、さらにそれはより多くの仕事をします。次の詳細な説明を参照してください。
更新: pgsql-performanceに関する重要な議論は、この投稿から生じました。このスレッドを参照してください。
更新2: これに役立つ改善が9.2beta3に追加されました。この投稿を参照してください。
TRUNCATE
の詳細な説明 vs DELETE FROM
:
このトピックの専門家ではありませんが、私の理解では、TRUNCATE
DELETE
はテーブルあたりのコストがほぼ固定されていますが、 n行に対して少なくともO(n)です。削除されるテーブルを参照する外部キーがある場合はさらに悪化します。
私はいつもTRUNCATE
の固定費を想定していました DELETE
のコストよりも低かった ほぼ空のテーブルにありますが、これはまったく当てはまりません。
TRUNCATE table;
DELETE FROM table;
以上のことを行います
TRUNCATE table
後のデータベースの状態 代わりに実行する場合とほとんど同じです:
-
DELETE FROM table;
-
VACCUUM (FULL, ANALYZE) table;
(9.0以降のみ、脚注を参照)
...もちろんTRUNCATE
DELETE
では実際にはその効果を達成しません およびVACUUM
。
重要なのは、DELETE
およびTRUNCATE
異なることを行うので、同じ結果の2つのコマンドを比較しているだけではありません。
DELETE FROM table;
デッドローとブロートを残すことを許可し、インデックスがデッドエントリを運ぶことを許可し、クエリプランナーによって使用されるテーブル統計を更新しません。
TRUNCATE
まるでCREATE
であるかのように完全に新しいテーブルとインデックスを提供します ed。これは、すべてのレコードを削除し、テーブルのインデックスを再作成して、VACUUM FULL
を実行したようなものです。 。
テーブルにクラッドが残っているかどうかを気にしない場合は、DELETE FROM table;
を使用した方がよい場合があります。 。
VACUUM
を実行していないため デッドローとインデックスエントリは、スキャンして無視する必要のある肥大化として蓄積されることがわかります。これにより、すべてのクエリが遅くなります。テストで実際に多くのデータが作成および削除されない場合は、気付かない、または気にしない可能性があり、いつでもVACUUM
を実行できます。 または、テスト実行の途中で2回実行します。より良いのは、積極的な自動真空設定により、自動真空がバックグラウンドで確実に実行されるようにすることです。
引き続きTRUNCATE
全体以降のすべてのテーブル テストスイートを実行して、多くの実行で効果が蓄積されないことを確認します。 9.0以降では、VACUUM (FULL, ANALYZE);
世界的には、少なくとも同じくらい良いとは言えませんが、はるかに簡単です。
IIRC Pgにはいくつかの最適化があります。つまり、テーブルを表示してブロックをすぐに空きとしてマークできるのはトランザクションだけである場合に気付く可能性があります。テストでは、膨張を作成したいとき、それを行うために複数の同時接続が必要でした。しかし、私はこれに頼りません。
DELETE FROM table;
f/k参照のない小さなテーブルには非常に安い
DELETE
へ 外部キー参照のないテーブルからのすべてのレコード。すべてのPgは、順次テーブルスキャンを実行し、xmax
を設定する必要があります。 遭遇したタプルの。これは非常に安価な操作です。基本的に、線形読み取りと半線形書き込みです。 AFAIKは、インデックスに触れる必要はありません。後でVACUUM
によってクリーンアップされるまで、死んだタプルを指し続けます。 また、死んだタプルのみを含むテーブル内のブロックを空きとしてマークします。
DELETE
たくさんある場合にのみ高価になります レコードの数、チェックする必要のある外部キー参照がたくさんある場合、または後続のVACUUM (FULL, ANALYZE) table;
TRUNCATE
と一致する必要があります DELETE
のコスト内での効果 。
ここでの私のテストでは、DELETE FROM table;
通常、TRUNCATE
より4倍高速でした 0.5ms対2msで。これはSSD上のテストDBであり、fsync=off
で実行されます。 このデータをすべて失ってもかまわないからです。もちろん、DELETE FROM table;
はすべて同じ作業を行っているわけではなく、VACUUM (FULL, ANALYZE) table;
21ミリ秒かかるので、DELETE
私が実際にテーブルの手付かずを必要としない場合にのみ勝利です。
TRUNCATE table;
DELETE
よりもはるかに多くの固定費の作業とハウスキーピングを行います
対照的に、TRUNCATE
多くの仕事をしなければなりません。テーブル、TOASTテーブル(存在する場合)、およびテーブルが持つすべてのインデックスに新しいファイルを割り当てる必要があります。ヘッダーはこれらのファイルに書き込む必要があり、システムカタログも更新する必要がある場合があります(その点についてはよくわかりませんが、チェックしていません)。次に、古いファイルを新しいファイルに置き換えるか、古いファイルを削除する必要があります。また、通常はすべてのバッファーをディスクにフラッシュする同期操作(fsync()など)を使用して、ファイルシステムが変更に追いついていることを確認する必要があります。 。 (データを食べる)オプションfsync=off
を使用して実行している場合、同期がスキップされるかどうかはわかりません 。
最近、TRUNCATE
であることを知りました また、古いテーブルに関連するすべてのPostgreSQLのバッファをフラッシュする必要があります。これは、巨大なshared_buffers
を使用すると、取るに足らない時間がかかる可能性があります。 。これが、CIサーバーで低速になる理由だと思います。
バランス
とにかく、TRUNCATE
TOASTテーブルが関連付けられているテーブル(ほとんどの場合)といくつかのインデックスには、少し時間がかかる場合があります。長くはありませんが、DELETE
より長くなります ほぼ空のテーブルから。
したがって、DELETE FROM table;
を実行した方がよい場合があります。 。
-
注:9.0より前のDBでは、CLUSTER table_id_seq ON table; ANALYZE table;
またはVACUUM FULL ANALYZE table; REINDEX table;
TRUNCATE
とほぼ同等になります 。 VACUUM FULL
implは9.0ではるかに優れたものに変更されました。