68

Postgresql 9.1.4dbサーバーとして使用しています。私は自分のテストスイートを高速化しようとしてきたので、何が起こっているのかを正確に確認するためにデータベースを少しプロファイリングしました。テストの最後に、database_cleanerを使用してテーブルを切り詰めています。はい、トランザクションの方が速いことは知っていますが、特定の状況ではトランザクションを使用できないので、気にしません。

私が懸念しているのは、TRUNCATION に (DELETE を使用するよりも) 時間がかかる理由と、CI サーバーでさらに長い時間がかかる理由です。

現在、ローカル (Macbook Air) では、完全なテスト スイートに 28 分かかります。テーブルを切り捨てるたびに、ログを追跡します...つまり:

TRUNCATE TABLE table1, table2  -- ... etc

切り捨ての実行には 1 秒以上かかります。CI サーバー (Ubuntu 10.04 LTS) でログを追跡すると、テーブルを切り捨てるのに 8 秒かかり、ビルドには 84 分かかります。

戦略に切り替えたとき:deletion、ローカル ビルドに 20 分かかり、CI サーバーは 44 分にダウンしました。これは大きな違いであり、その理由については本当に驚きました。CI サーバーのDBを調整 しました。16 GB のシステム RAM、4 GB の共有バッファー...、および SSD が搭載されています。すべての良いもの。どのように可能ですか:

を。
2GBのRAM bを搭載した私のMacbook Airよりもはるかに遅い. TRUNCATION は DELETE よりもはるかに遅いため、postgresql のドキュメント では、はるかに高速である必要があると明示的に述べられています。

何かご意見は?

4

4 に答える 4

155

これは、SO と PostgreSQL メーリング リストの両方で、最近数回出てきました。

最後の 2 つのポイントのTL;DR :

(a) より大きな shared_buffers が、CI サーバーで TRUNCATE が遅くなる理由である可能性があります。fsync の構成が異なるか、SSD の代わりに回転メディアを使用していることが原因である可能性もあります。

(b)TRUNCATE固定コストですが、必ずしも より遅いわけDELETEではありません。さらに、より多くの作業を行います。以下の詳細な説明を参照してください。

更新: pgsql-performance に関する重要な議論は、この投稿から生じました。このスレッドを参照してください。

更新 2:これに役立つ改善が 9.2beta3 に追加されました。この投稿を参照してください。

TRUNCATEvsの詳細な説明DELETE FROM:

このトピックの専門家ではありませんが、私の理解では、TRUNCATEテーブルあたりのコストはほぼ固定されていますが、DELETEn 行に対して少なくとも 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でその効果を達成するわけではありません。DELETEVACUUM

要点は、DELETETRUNCATEが異なることを行うということです。そのため、同じ結果を持つ 2 つのコマンドを比較するだけではありません。

ADELETE FROM table;は、無効な行と膨張をそのままにし、インデックスが無効なエントリを保持できるようにし、クエリ プランナーが使用するテーブル統計を更新しないなどです。

ATRUNCATEは、あたかもCREATEed であるかのように、完全に新しいテーブルとインデックスを提供します。すべてのレコードを削除し、テーブルを再インデックスして、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;に一致するために必要な後続の数を数えた場合にのみ、コストがかかります。TRUNCATEDELETE

ここでの私のテストでは、通常、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 でより優れたものに変更されました。

于 2012-07-11T00:24:39.190 に答える
5

ブラッド、ただあなたに知らせるために。私は非常によく似た質問をかなり深く調べました。

関連する質問:行数の少ない 30 個のテーブル - それらを空にし、接続されたシーケンスをリセットする最速の方法は TRUNCATE ですか?

この問題とこのプル リクエストも参照してください。

https://github.com/bmabey/database_cleaner/issues/126

https://github.com/bmabey/database_cleaner/pull/127

また、このスレッド: http://archives.postgresql.org/pgsql-performance/2012-07/msg00047.php

これを回答として書いて申し訳ありませんが、すでにコメントが多すぎるためか、コメントのリンクが見つかりませんでした。

于 2012-07-14T10:50:25.820 に答える
0

考慮すべきいくつかの代替アプローチ:

  • 静的な「フィクスチャ」データを含む空のデータベースを作成し、その中でテストを実行します。完了したら、データベースをドロップするだけです。これは高速であるはずです。
  • テーブル名と主キーIDの列を含む「test_ids_to_delete」という新しいテーブルを作成します。削除ロジックを更新して、代わりにこのテーブルにID /テーブル名を挿入します。これにより、削除を実行するよりもはるかに高速になります。次に、「オフライン」で実行するスクリプトを作成して、テストの実行全体が終了した後、または一晩でデータを実際に削除します。

前者は「クリーンルーム」アプローチですが、後者は、一部のテストデータがデータベースに長期間保持されることを意味します。オフライン削除を使用する「ダーティ」アプローチは、約20,000のテストを含むテストスイートに使用しているものです。はい、開発データベースに「余分な」テストデータがあるために問題が発生することもありますが、問題が発生することもあります。しかし、この「汚れ」は、クリーンルームアプローチでは決して実現できない方法で、「乱雑さ」が実際の状況をより適切にシミュレートするため、バグを見つけて修正するのに役立つ場合があります。

于 2012-07-10T18:39:29.660 に答える