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