16

I have the following table and indices defined:

CREATE TABLE ticket (
  wid bigint NOT NULL DEFAULT nextval('tickets_id_seq'::regclass),
  eid bigint,
  created timestamp with time zone NOT NULL DEFAULT now(),
  status integer NOT NULL DEFAULT 0,
  argsxml text,
  moduleid character varying(255),
  source_id bigint,
  file_type_id bigint,
  file_name character varying(255),
  status_reason character varying(255),
  ...
)

I created an index on the created timestamp as follows:

CREATE INDEX ticket_1_idx
  ON ticket
  USING btree
  (created );

Here's my query:

select * from ticket 
where created between '2012-12-19 00:00:00' and  '2012-12-20 00:00:00'

This was working fine until the number of records started to grow (about 5 million) and now it's taking forever to return.

Explain analyze reveals this:

Index Scan using ticket_1_idx on ticket  (cost=0.00..10202.64 rows=52543 width=1297) (actual time=0.109..125.704 rows=53340 loops=1)
  Index Cond: ((created >= '2012-12-19 00:00:00+00'::timestamp with time zone) AND (created <= '2012-12-20 00:00:00+00'::timestamp with time zone))
Total runtime: 175.853 ms

So far I've tried setting:

random_page_cost = 1.75 
effective_cache_size = 3 

Also created:

create CLUSTER ticket USING ticket_1_idx;

Nothing works. What am I doing wrong? Why is it selecting sequential scan? The indexes are supposed to make the query fast. Anything that can be done to optimize it?

4

1 に答える 1

29

CLUSTER

を使用する場合CLUSTER、表示される構文は無効です。

create CLUSTER ticket USING ticket_1_idx;

1 回実行:

CLUSTER ticket USING ticket_1_idx;

これ、より大きな結果セットで大いに役立ちます。1 行または数行が返される場合はそれほど多くはありません。
Postgres は、後続の呼び出しに使用するインデックスを記憶しています。テーブルが読み取り専用でない場合、時間の経過とともに効果が低下するため、一定の間隔で再実行する必要があります。

CLUSTER ticket;

おそらく揮発性パーティションでのみ。下記参照。

ただし、更新が多い場合、CLUSTER(またはVACUUM FULL) は実際にはパフォーマンスに悪い可能性があります。適切な量​​の膨張によりUPDATE、新しい行バージョンを同じデータ ページに配置でき、基礎となる物理ファイルを頻繁に物理的に拡張する必要がなくなります。慎重に調整された を使用してFILLFACTOR、両方の長所を活かすことができます。

pg_repack/pg_squeeze

CLUSTERこれは、マルチユーザー環境では問題になる可能性があります。マニュアルの引用:

テーブルがクラスター化されている場合、ACCESS EXCLUSIVEロックが取得されます。これにより、他のデータベース操作 (読み取りと書き込みCLUSTERの両方)が終了するまでテーブルで操作できなくなります。

大胆強調鉱山。代替案を検討してください!

pg_repack:

とは異なりCLUSTERVACUUM FULL処理中に処理されたテーブルの排他ロックを保持することなく、オンラインで動作します。CLUSTERpg_repack は起動が効率的で、パフォーマンスは直接使用する場合と同等です。

と:

pg_repack は、再編成の最後に排他ロックを取得する必要があります。

現在のバージョン 1.4.7 は PostgreSQL 9.4 ~ 14 で動作します。

pg_squeezeは、次のように主張する新しい代替手段です。

pg_repack実際、拡張子を置き換えようとしています。

現在のバージョン 1.4 は Postgres 10 ~ 14 で動作します。

クエリ

クエリは単純なので、それ自体でパフォーマンスの問題が発生することはありません。

ただし、正確性について一言:BETWEEN構造には境界が含まれます。このクエリは、12 月 19 日のすべてと、12 月 20 日 00:00 のレコードを選択します。これは非常にありそうもない要件です。チャンスは、あなたが本当に欲しいものです:

SELECT *
FROM   ticket 
WHERE  created >= '2012-12-19 0:0'
AND    created <  '2012-12-20 0:0';

パフォーマンス

まず、次のように尋ねます。

シーケンシャル スキャンを選択するのはなぜですか?

出力は、順次テーブル スキャンではなく、Index ScanEXPLAINを明確に示しています。なんらかの誤解があるはずです。

パフォーマンスを改善できるかもしれませんが、必要な背景情報は問題ではありません。可能なオプションは次のとおりです。

  • *転送コスト (およびその他のパフォーマンス上の利点) を削減する代わりに、必要な列のみをクエリします。

  • パーティショニングを見て、実用的なタイム スライスを別のテーブルに入れます。必要に応じてパーティションにインデックスを追加します。

  • パーティショニングがオプションでない場合は、別の関連するが邪魔にならない手法として、1 つ以上の部分インデックスを追加する方法があります。
    たとえば、主に当月のクエリを実行する場合、次の部分インデックスを作成できます。

      CREATE INDEX ticket_created_idx ON ticket(created)
      WHERE created >= '2012-12-01 00:00:00'::timestamp;
    

    CREATE新しい月が始まる直前の新しいインデックス。cron ジョブを使用して、タスクを簡単に自動化できます。オプションDROPで、古い月の部分インデックス。

    合計インデックスを保持しCLUSTERます (部分インデックスでは操作できません)。古いレコードが変更されない場合、新しいパーティションを再クラスタ化するだけでよいため、テーブルのパーティション分割がこのタスクに大いに役立ちます。
    繰り返しになりますが、レコードがまったく変更されない場合は、おそらく必要ありませんCLUSTER

パフォーマンスの基本

基本の1つが欠けている可能性があります。通常のパフォーマンスに関するアドバイスがすべて適用されます。

于 2012-12-23T01:28:26.617 に答える