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


37

Ubuntu 12.04でPG 9.1を使用します。

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

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

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

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

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

PostgreSQLのデータセットを何らかの方法で再インポートするカスタムプログラムを作成する以外に、バルクUPDATEパフォーマンスをインポートのパフォーマンスに近づけるためにできることはありますか?(これは、ログ構造化されたマージツリーがうまく処理できると信じている領域ですが、PostgreSQL内でできることはないかと考えています。)

いくつかのアイデア:

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

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

テーブルには同時ロードがありますが、読み取り専用です。


あなたの質問に重要な情報がありません:あなたのPostgresのバージョンは?値はどこから来ますか?データベース外のファイルのように聞こえますが、明確にしてください。ターゲット表に同時ロードがありますか?はいの場合、正確には何ですか?または、ドロップして再作成する余裕はありますか?外部キーはありません、OK-しかし、ビューのような他の依存オブジェクトはありますか?不足している情報を使用して質問を編集してください。コメントで絞らないでください。
アーウィンブランドステッター

@ErwinBrandstetterありがとう、私の質問を更新しました。
ヤン

explain analyzeルックアップにインデックスを使用していることを確認したと思いますか?
-rogerdpack

回答:


45

仮定

Qに情報がないため、次のことを想定します。

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

溶液

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

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

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

データベースUPDATE一時テーブルからの単一のビッグは、データベースの外部からの個々の更新よりも数桁高速です。

ではPostgreSQLのMVCCモデルUPDATE新しい行バージョンを作成し、削除したとして、古いものをマークするための手段。それは、INSERTと同じくらい高価DELETEです。さらに、大量のタプルが残っています。とにかくテーブル全体を更新しているため、新しいテーブルを作成して古いテーブルを削除する方が全体的に高速です。

使用可能な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更新も可能になります

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

 


1
実際には、ターゲットテーブルの行の20%を更新しているだけです。すべてではありませんが、ランダム更新シークよりもマージの方がおそらく十分に大きい部分です。
ヤン

1
@AryehLeibTaurog:DROP TABLEを取り出すので、それは起きてはいけませんAccess Exclusive Lock。いずれにせよ、答えの一番上に前提条件を既にリストしましたYou can afford to drop and recreate the target table.。トランザクションの開始時にテーブルをロックすると役立つ場合があります。状況のすべての関連する詳細を含む新しい質問を開始することをお勧めします。そうすれば、私たちはこれの一番下に到達できます。
アーウィンブランドステッター14

1
@ErwinBrandstetter興味深い。サーバーのバージョンに依存するようです。psycopg2アダプタpsqlクライアントを使用して、 8.4および9.1でエラーを再現しました。9.3ではエラーはありません。最初のスクリプトのコメントを参照してください。ここに投稿する質問があるかどうかはわかりませんが、postgresqlリストの1つに関する情報を求める価値があるかもしれません。
アリーエレイブタウログ14

1
私が書いた簡単なヘルパークラスをプロセスを自動化するpythonで。
アリーエレイブタウログ

3
非常に便利な答え。わずかに変形例として、一つは元の表から、更新されるだけ更新される列と参照の列、削除列を持つ一時テーブルを作成してもよいし、テーブルを使用してマージCREATE TABLE tbl_new AS SELECT t.*, u.field1, u.field2 from tbl t NATURAL LEFT JOIN tmp_tbl u;LEFT JOIN更新が無いいる行を保持することを可能にします。もちろん、NATURAL有効なUSING()またはに変更できますON
スキッピールグラングロウ14

2

構造化ファイルでデータを利用できる場合は、外部データラッパーを使用して読み取り、ターゲットテーブルでマージを実行できます。


3
「ターゲットテーブルにマージする」とは具体的にどういう意味ですか?(元の質問の3番目の箇条書きで提案されているように)一時テーブルにコピーするよりもFDWを使用する方が良いのはなぜですか?
ヤン

MERGE SQLステートメントのように「マージ」。FDWを使用すると、一時テーブルにデータをコピーする追加のステップなしでそれを行うことができます。データセット全体を置き換えるのではなく、現在のデータセットからの変更を表さない特定の量のデータがファイルにあると仮定しています-かなりの量が変更された場合、完全なテーブルの交換は価値があるかもしれません。
デビッドアルドリッジ

1
@DavidAldridge:SQL:2003標準で定義されていますMERGEが、PostgreSQLにはまだ実装されていません。他のRDBMSの実装はかなり異なります。以下のためのタグ情報を検討MERGEしてUPSERT
アーウィンブランドステッター

@ErwinBrandstetter [glurk]ああそうだね。まあマージは本当に私が思うケーキの上のアイシングです。import-to-temporary-table-stepを使用せずにデータにアクセスすることは、実際にはFDWテクニックの要点です。
デビッドアルドリッジ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.