web-dev-qa-db-ja.com

300万行のPostgreSQLデータベースでの遅い単純な更新クエリ

私はシンプルなUPDATE table SET column1 = 0 Postegres 8.4に約300万行のテーブルがあるが、完了するまでに時間がかかります。 10分以上実行されています。今私の最後の試みで。

以前は、そのテーブルでVACUUMコマンドとANALYZEコマンドを実行しようとしましたが、いくつかのインデックスも作成しようとしました(ただし、これでこの場合に違いが生じることはないと思います)が、役に立たないようです。

他のアイデアはありますか?

ありがとう、リカルド

更新:

これはテーブル構造です:

CREATE TABLE myTable
(
  id bigserial NOT NULL,
  title text,
  description text,
  link text,
  "type" character varying(255),
  generalFreq real,
  generalWeight real,
  author_id bigint,
  status_id bigint,
  CONSTRAINT resources_pkey PRIMARY KEY (id),
  CONSTRAINT author_pkey FOREIGN KEY (author_id)
      REFERENCES users (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT c_unique_status_id UNIQUE (status_id)
);

実行しようとしていますUPDATE myTable SET generalFreq = 0;

32
Ricardo

この回答を見てみましょう: PostgreSQLは、配列と多くの更新を含む大きなテーブルでは低速です

まず、より良いFILLFACTORから始め、VACUUM FULLを実行してテーブルを強制的に書き換え、UPDATEクエリの後でHOT更新を確認します。

SELECT n_tup_hot_upd, * FROM pg_stat_user_tables WHERE relname = 'myTable';

更新するレコードが多い場合、HOT更新ははるかに高速です。 HOTの詳細については、この 記事 を参照してください。

Ps。バージョン8.3以降が必要です。

16
Frank Heikens

10億行または20億行のテーブルを各行のさまざまな値で更新する必要があります。 1回の実行で約1億回の変更(10%)が行われます。パーティションを使用する場合、Postgresqlは準備されたクエリを常に最適化するとは限らないため、私の最初の試みは、特定のパーティションで直接300Kの更新のトランザクションでそれらをグループ化することでした。

  1. 「UPDATE myTable SET myField = value WHERE myId = id」の束のトランザクション
    与える1,5更新/秒つまり、各実行には少なくとも18時間かかります。
  2. FILLFACTOR = 50を使用して、ここで説明するようにHOT更新ソリューション。毎秒1,600回の更新を提供します。 SSDを使用しているので、ストレージサイズが2倍になるため、コストがかかります。
  3. 更新された値の一時テーブルに挿入し、それらをUPDATE ... FROMでマージした後18,0更新/秒を提供します。各パーティションにVACUUMを実行すると、それ以外の場合は100,000アップ/秒。やったー.
    操作のシーケンスは次のとおりです:

CREATE TEMP TABLE tempTable (id BIGINT NOT NULL, field(s) to be updated,
CONSTRAINT tempTable_pkey PRIMARY KEY (id));

利用可能なRAMがいっぱいになったとき、またはテーブル/パーティションの変更が必要になったとき、または完了したときに、バッファに一連の更新を蓄積します。

COPY tempTable FROM buffer;
UPDATE myTable a SET field(s)=value(s) FROM tempTable b WHERE a.id=b.id;
COMMIT;
TRUNCATE TABLE tempTable;
VACUUM FULL ANALYZE myTable;

つまり、1億回の更新では、バキュームを含めて、実行に18時間ではなく1.5時間かかります。

35
Le Droid

35分待った後。私のUPDATEクエリを完了するために(まだ完了していません)、別の方法を試すことにしました。だから私がしたことはコマンドでした:

CREATE TABLE table2 AS 
SELECT 
  all the fields of table1 except the one I wanted to update, 0 as theFieldToUpdate
from myTable

次に、インデックスを追加し、古いテーブルを削除して、新しいテーブルの名前を変更します。わずか1.7分で完了しました。処理し、インデックスと制約を再作成するための追加の時間を加えます。しかし、それは助けになりました! :)

もちろん、それは他の誰もデータベースを使用していなかったという理由だけで機能しました。これが実稼働環境にある場合は、最初にテーブルをロックする必要があります。

7
Ricardo

今日、私は同様の問題で何時間も過ごしました。私は solutionを見つけました。更新前にすべての制約/インデックスを削除します。更新される列がインデックス付けされているかどうかに関係なく、psqlは更新されたすべての行のすべてのインデックスを更新するようです。更新が完了したら、制約/インデックスを追加して戻します。

2
Tregoreg

これを試してください(generalFreqはREALタイプとして始まり、同じままです):

ALTER TABLE myTable ALTER COLUMN generalFreq TYPE REAL USING 0;

これにより、DROP + CREATEと同様にテーブルが書き換えられ、すべてのインデックスが再構築されます。しかし、すべて1つのコマンドで。ずっと高速(約2倍)であり、依存関係を処理したり、インデックスやその他のものを再作成したりする必要はありません。または、他のすべてをその後ろにキューイングしたい場合は、それが望ましいでしょう。 「多すぎる」行を更新していない場合、この方法は単なる更新よりも遅くなります。

2
Fabiano Bonin

私が提案する最初のこと(から https://dba.stackexchange.com/questions/118178/does-updating-a-row-with-the-same-value-actually-update-the-row )は、「必要」な行のみを更新することです。例:

 UPDATE myTable SET generalFreq = 0 where generalFreq != 0;

(generalFreqのインデックスも必要になる場合があります)。次に、更新する行を減らします。値がすべてすでにゼロでない場合はそうではありませんが、値を変更したかどうかに関係なく、行とすべてのインデックスを更新するため、より少ない行を更新すると「助けになります」。

別のオプション:デフォルトとnull以外の制約に関して星が整列している場合、古い列を削除して、メタデータを調整するだけで another を作成できます。

1
rogerdpack

どのように実行していますか?各行をループしてupdateステートメントを実行している場合、数百万もの個別の更新を実行している可能性があるため、実行速度は非常に遅くなります。

1つのステートメントのすべてのレコードに対して単一の更新ステートメントを実行している場合、それは非常に速く実行され、このプロセスが遅い場合は、おそらく他の何よりもハードウェアにダウンしています。 300万はたくさんの記録です。

0
Tom Gullen

私のテストでは、一時的なテーブルを使用していても、200 000行を超える大きな更新は100 000行の2回の更新よりも遅いことに気付きました。

私の解決策はループすることです。各ループで200 000行の一時テーブルを作成し、このテーブルで値を計算してから、メインテーブルを新しい値で更新します...

2 000 000行ごとに、手動で "VACUUM ANALYZE mytable"を実行しましたが、自動バキュームはそのような更新では機能しないことに気付きました。

0
Rolintocour