web-dev-qa-db-ja.com

PostgreSQLでの一括更新パフォーマンスの最適化

Ubuntu 12.04でのPG 9.1の使用。

現在、データベースで大量のUPDATEステートメントを実行するには、最大24時間かかります。

UPDATE table
SET field1 = constant1, field2 = constant2, ...
WHERE id = constid

(IDで識別されるオブジェクトのフィールドを上書きしているだけです。)値は外部データソースから取得されます(テーブル内のDBにはまだありません)。

テーブルにはそれぞれ少数のインデックスがあり、外部キー制約はありません。最後までCOMMITは行われません。

DB全体のpg_dumpをインポートするには2時間かかります。これは、私たちが合理的に対象とすべきベースラインのようです。

PostgreSQLのデータセットを何らかの方法で再インポートして再インポートするカスタムプログラムを作成する前に、一括更新のパフォーマンスをインポートのパフォーマンスに近づけるためにできることはありますか? (これは、ログ構造のマージツリーがうまく機能すると私たちが信じている領域ですが、PostgreSQL内で私たちにできることがあるかどうか疑問に思っています。)

いくつかのアイデア:

  • すべての非IDインデックスを削除し、後で再構築しますか?
  • checkpoint_segmentsを増やしますが、これは実際に長期スループットの維持に役立ちますか?
  • ここで述べたテクニック ? (新しいデータをテーブルとしてロードし、新しいデータにIDが見つからない古いデータを「マージ」します)

基本的に、試すことはたくさんありますが、最も効果的なものは何か、他のことを見落としているかどうかはわかりません。次の数日間は実験に費やしますが、ここでも質問したいと思いました。

テーブルに同時負荷がありますが、それは読み取り専用です。

40
Yang

仮定

Qに情報が不足しているので、次のように想定します。

  • データは、データベースサーバー上のファイルから取得されます。
  • データはCOPY出力と同様にフォーマットされ、ターゲットテーブルと一致する行ごとにuniqueidが付けられます。
    そうでない場合は、最初に適切にフォーマットするか、COPYオプションを使用してフォーマットを処理します。
  • ターゲットテーブルのすべての行またはほとんどの行を更新しています。
  • ターゲット表をドロップして再作成する余裕があります。
    つまり、no同時アクセスです。そうでない場合は、この関連する回答を検討してください。
  • インデックスを除いて、依存するオブジェクトはまったくありません。

解決

番目の箇条書きのリンク で概説されているのと同様のアプローチを使用することをお勧めします。主要な最適化。

一時テーブルを作成するには、より簡単で高速な方法があります。

CREATE TEMP TABLE tmp_tbl AS SELECT * FROM tbl LIMIT 0;

一時テーブル内部からの単一の大きなUPDATEは、データベースの外部からの個別の更新よりも数桁高速です。

PostgreSQLのMVCCモデル では、UPDATEは新しい行バージョンを作成し、古い行バージョンを削除済みとしてマークすることを意味します。これは、INSERTDELETEを組み合わせた場合と同じくらいの費用がかかります。さらに、それはあなたにたくさんの死んだタプルを残します。とにかくテーブル全体を更新しているので、新しいテーブルを作成して古いテーブルを削除する方が全体的に速くなります。

If十分なRAMが利用できる場合は、 temp_buffers を設定します(このセッションのみ! )一時テーブルをRAMに保持するのに十分な高さ-他に何かする前。

RAMの必要量を見積もるには、小さなサンプルでテストを実行し、 dbオブジェクトサイズ関数 を使用します。

SELECT pg_size_pretty(pg_relation_size('tmp_tbl'));  -- complete size of table
SELECT pg_column_size(t) FROM tmp_tbl t LIMIT 10;  -- size of sample rows

完全なスクリプト

SET temp_buffers = '1GB';        -- example value

CREATE TEMP TABLE tmp_tbl AS SELECT * FROM tbl LIMIT 0;

COPY tmp_tbl FROM '/absolute/path/to/file';

CREATE TABLE tbl_new AS
SELECT t.col1, t.col2, u.field1, u.field2
FROM   tbl     t
JOIN   tmp_tbl u USING (id);

-- Create indexes like in original table
ALTER TABLE tbl_new ADD PRIMARY KEY ...;
CREATE INDEX ... ON tbl_new (...);
CREATE INDEX ... ON tbl_new (...);

-- exclusive lock on tbl for a very brief time window!
DROP TABLE tbl;
ALTER TABLE tbl_new RENAME TO tbl;

DROP TABLE tmp_tbl; -- will also be dropped at end of session automatically

同時負荷

テーブル名がOIDすぐに、しかし新しいテーブルに別のOIDがあります。テーブルは一貫性を保ちますが、並行操作は例外を受け取り、繰り返す必要がある場合があります。この関連する回答の詳細:

ルートを更新

UPDATEルートに移動する必要がある場合は、更新中に不要なインデックスを削除し、後で再作成します。個々の行ごとにインデックスを更新するよりも、1つのピースでインデックスを作成する方がはるかに安価です。 HOTの更新 も可能です。

UPDATEを使用した同様の手順の概要を この密接に関連するSOの回答 で説明しました。

48

データを構造化ファイルで利用できるようにする場合は、 foreign data wrapper を使用してデータを読み取り、ターゲットテーブルでマージを実行できます。

2
David Aldridge