7

次のUPDATEクエリがあります。

UPDATE Indexer.Pages SET LastError=NULL where LastError is not null;

現在、このクエリが完了するまでに約 93 分かかります。これをもう少し速くする方法を見つけたいと思います。

Indexer.Pagesテーブルには約 506,000 行あり、そのうち約 490,000 行に の値が含まれているため、LastErrorここでインデックスを利用できるとは思えません。

テーブル (非圧縮時) には約 46 GB のデータがありますが、そのデータの大部分は というテキスト フィールドにありますhtml。多くのページをロードおよびアンロードするだけで、速度が低下していると思います。1 つのアイデアは、とフィールドだけで新しいテーブルを作成し、できるだけ小さく保つことです。ただし、テーブルのコピーを作成するためのハード ディスク領域が実際にはないため、この理論をテストするのはかなりの作業量になります。それを別のマシンにコピーし、テーブルをドロップしてから、データをコピーして戻す必要があり、おそらく一晩中かかります。IdhtmlIndexer.Pages

アイデア?Postgres 9.0.0 を使用しています。

アップデート:

スキーマは次のとおりです。

CREATE TABLE indexer.pages
(
  id uuid NOT NULL,
  url character varying(1024) NOT NULL,
  firstcrawled timestamp with time zone NOT NULL,
  lastcrawled timestamp with time zone NOT NULL,
  recipeid uuid,
  html text NOT NULL,
  lasterror character varying(1024),
  missingings smallint,
  CONSTRAINT pages_pkey PRIMARY KEY (id ),
  CONSTRAINT indexer_pages_uniqueurl UNIQUE (url )
);

また、2 つのインデックスがあります。

CREATE INDEX idx_indexer_pages_missingings
  ON indexer.pages
  USING btree
  (missingings )
  WHERE missingings > 0;

CREATE INDEX idx_indexer_pages_null
  ON indexer.pages
  USING btree
  (recipeid )
  WHERE NULL::boolean;

このテーブルにはトリガーがなく、FK 制約を持つ別のテーブルが 1 つありますPages.PageId

4

3 に答える 3

6

@kgrittnがコメントとして投稿したものが、これまでのベストアンサーです。私は単に詳細を記入しているだけです。

他のことをする前に、 PostgreSQLを現在のバージョンにアップグレードする必要があり ます。少なくともメジャーバージョンの最後のセキュリティリリースにアップグレードする必要があります。プロジェクトのガイドラインを参照してください。

また、Kevinが列を含むインデックスについて言及したことを強調したいと思いますLastError。通常、HOT更新は、データページのデッド行をリサイクルし、更新を大幅に高速化できます。つまり、バキュームの必要性(ほとんど)を効果的に排除できます。関連している:

列が何らかの方法でインデックスで使用されている場合、インデックスが破損するため、HOTUPDATEは無効になります。その場合は、前にこれらのインデックスをすべて削除し、後で再作成することで、クエリを大幅に高速化できるはずです。UPDATE

このコンテキストでは、複数の小さなUPDATEを実行すると役立ちます。次 の場合...
...更新された列がインデックスに含まれていない(HOT更新を有効にする)。...複数のトランザクションUPDATEで複数のパッチに簡単に分割できます。...これらのパッチの行は、テーブル全体に分散されています(物理的に、論理的にではありません)。...デッドタプルの再利用を妨げる他の同時トランザクションはありません。

VACCUUMHOT更新ではデッドタプルを直接再利用できるため、複数のパッチの間に配置する必要はありません。同じトランザクションまたは同時トランザクションからではなく、以前のトランザクションからのデッドタプルのみを再利用できます。操作の最後にをスケジュールするVACUUMか、自動バキューム処理を実行させることができます。

-に必要のない他のインデックスでも同じことができUPDATEます。数値から判断するとUPDATE、とにかくインデックスを使用することはありません。テーブルの大部分を更新する場合、新しいインデックスを最初から作成する方が、行を変更するたびにインデックスを段階的に更新するよりもはるかに高速です。

また、更新によって外部キーの制約が破られる可能性はほとんどありません。それらを削除して再作成することもできます。これにより、参照整合性が適用されないタイムスロットが開かれます。中に整合性に違反UPDATEすると、FKを再作成しようとしたときにエラーが発生します。すべてを1つのトランザクション内で実行する場合、並行トランザクションはドロップされたFKを確認できませんが、テーブルの書き込みロックを取得します-インデックスまたはトリガーのドロップ/再作成と同じです)

最後に、更新に必要のないトリガーを無効にしてから有効にします。

これらすべてを1つのトランザクションで実行してください。たぶん、いくつかの小さなパッチでそれを行うので、並行操作を長時間ブロックしません。

それで:

BEGIN;
ALTER TABLE tbl DISABLE TRIGGER user; -- disable all self-made triggers
-- DROP indexes (& fk constraints ?)
-- UPDATE ...
-- RECREATE indexes (& fk constraints ?)
ALTER TABLE tbl ENABLE TRIGGER user;
COMMIT;

VACUUMトランザクションブロック内で実行することはできません。ドキュメントごと:

VACUUMトランザクションブロック内では実行できません。

操作をいくつかの大きなチャンクに分割し、その間に実行することができます。

VACUUM ANALYZE tbl;

同時トランザクションを処理する必要がない場合は、(さらに効果的に)次のことができます。

ALTER TABLE tbl DISABLE TRIGGER user; -- disable all self-made triggers
-- DROP indexes (& fk constraints ?)

-- Multiple UPDATEs with logical slices of the table
-- each slice in its own transaction.
-- VACUUM ANALYZE tbl;  -- optionally in between, or autovacuum kicks in

-- RECREATE indexes (& fk constraints ?)
ALTER TABLE tbl ENABLE TRIGGER user;
于 2012-06-18T18:19:28.687 に答える
1
UPDATE Indexer.Pages 
  SET LastError=NULL
  ;

NULL フィールドは既に NULL であるため、where 句は必要ありません。したがって、再度 NULL に設定しても害はありません (これがパフォーマンスに大きな影響を与えるとは思いません)。

number_of_rows = 500K およびテーブル サイズ = 46G とすると、平均行サイズは 90KB であると結論付けます。それは巨大です。テーブルの {unused, sparse} 列を他のテーブルに移動できますか?

于 2012-06-18T16:50:22.830 に答える
0

あなたの理論はおそらく正しいです。テーブル全体を読み取る (そして何かを実行する) と、速度が低下する可能性があります。

PageId と LastError を持つ別のテーブルを作成してみませんか? これを、現在のテーブルのデータで初期化します (所要時間は 93 分未満です)。次に、新しいテーブルの LastError を使用します。

暇なときに、既存のテーブルから LastError を削除できます。

ところで、通常は、列の 2 つのコピーを 2 つの別々のテーブルに保持することはお勧めしません。ただし、この場合、行き詰まっており、先に進む方法が必要なように聞こえます。

于 2012-06-18T16:15:10.640 に答える