2017-02-08 24 views
4

私はCentOS 6.7でpostgresql 9.4を実行しています。テーブルの一つは、レコードの数百万人の多くが含まれ、これはDDLです:PostgreSQL - インデックスを使用した非常に遅いフェッチ

CREATE TABLE domain.examples (
    id SERIAL, 
    sentence VARCHAR, 
    product_id BIGINT, 
    site_id INTEGER, 
    time_stamp BIGINT, 
    category_id INTEGER, 
    CONSTRAINT examples_pkey PRIMARY KEY(id) 
) 
WITH (oids = false); 

CREATE INDEX examples_categories ON domain.examples 
    USING btree (category_id); 

CREATE INDEX examples_site_idx ON domain.examples 
    USING btree (site_id); 

データはページネーションを使用して、私たちは1000年、レコードのバルクを取得していることを行う消費アプリケーション。しかし、インデックス付きの列をフェッチしても、取得時間は非常に遅いです。

explain analyze 
select * 
from domain.examples e 
where e.category_id = 105154 
order by id asc 
limit 1000; 

Limit (cost=0.57..331453.23 rows=1000 width=280) (actual time=2248261.276..2248296.600 rows=1000 loops=1) 
    -> Index Scan using examples_pkey on examples e (cost=0.57..486638470.34 rows=1468199 width=280) (actual time=2248261.269..2248293.705 rows=1000 loops=1) 
     Filter: (category_id = 105154) 
     Rows Removed by Filter: 173306740 
Planning time: 70.821 ms 
Execution time: 2248328.457 ms 

なぜ低速クエリが発生していますか?どのように改善することができますか?

ありがとうございます!私は本当に

+0

は、これらすべての '_id'の列が外部キーであるために仮定されていますか?彼らはそのように宣言されていないようです。 'sentence'の内容はどれくらいですか?キャッシュが寒かったり、サーバーのディスクが過負荷になっている可能性があります。もう一回やってみよう。 – Schwern

+0

そのように宣言されると、パフォーマンスが向上するはずですか?フェッチはそのテーブルからのみ行われ、結合は含まれません。 'sentence'は非常に短い文字列であり、何度も何度も同じ結果が得られます。 – Seffy

+1

有効な統計情報はありますか? - >> 'VACUUM ANALYZE domain.examples;' BTWはカーディナリティの低い 'e.category_id'ですか? – wildplasser

答えて

1

はCATEGORY_IDとid:

CREATE INDEX examples_site_idx2 ON domain.examples 
    USING btree (category_id, id); 

I 300万行にクエリを分析し説明してみてください。

                QUERY PLAN                 
---------------------------------------------------------------------------------------------------------------------------------------------- 
Limit (cost=0.43..9234.56 rows=1000 width=60) (actual time=0.655..597.193 rows=322 loops=1) 
    -> Index Scan using examples_pkey on examples e (cost=0.43..138512.43 rows=15000 width=60) (actual time=0.654..597.142 rows=322 loops=1) 
     Filter: (category_id = 105154) 
     Rows Removed by Filter: 2999678 
Planning time: 2.295 ms 
Execution time: 597.257 ms 
(6 rows) 

新しいインデックス付き::古いインデックス付き

                QUERY PLAN                  
------------------------------------------------------------------------------------------------------------------------------------------------- 
Limit (cost=0.43..2585.13 rows=1000 width=60) (actual time=0.027..28.814 rows=322 loops=1) 
    -> Index Scan using examples_site_idx2 on examples e (cost=0.43..38770.93 rows=15000 width=60) (actual time=0.026..28.777 rows=322 loops=1) 
     Index Cond: (category_id = 105154) 
Planning time: 1.471 ms 
Execution time: 28.860 ms 
(5 rows) 
+0

ありがとう!魅力として働く:-) – Seffy

1

これは、あなたが望むの計画はありませんが、PostgreSQLはインデックス全体examples_pkeyをスキャンし、条件category_id = 105154を持つレコードをフィルタリングされ、あなたがANALYZEでテーブルの上に、より良い統計を取得またはシステムのGUCsで遊んで試みることができます(プランナーに適切なインデックスを選択させるようにしてください。

category_id = 105154の行数があまり高くなければ、CTEを最初に使用することをお勧めします。そのため、プランナーはexamples_categoriesインデックスを使用する必要があります。

with favorite_category as (
    select * 
    from domain.examples e 
    where e.category_id = 105154) 
select * 
from favorite_category 
order by id asc 
limit 1000; 

これはcategory_id = 105154ですべてのレコードをフェッチするとidによってメモリの並べ替えで行う(つまりフェッチのサイズは、あなたの作業メモリよりも小さい場合、show work_mem;はそれが何であるかを見るために。デフォルトは4メガバイトです)。あなたは両方のフィールドにインデックスを作成することができます

+0

Postgresの場合、CTEは一般的に*パフォーマンスの問題。 – wildplasser

+0

@wilplasser私は同意します!しかし、この場合、category_id = 105154の行数が少ないと意味があると思います。 また、私は不思議です、あなたの主張を支持するための例を提供できますか?私は同意しないと言っているわけではありません! –

+0

この場合、CTEのないクエリはさらに多くの利益を得ることができます。 (CTEはオプトマイザーの障壁になります)しかし、この場合、おそらく違いはありません。 – wildplasser

関連する問題