2011-01-13 8 views
11

私は約2,000万行のテーブルを持っています。議論のために、テーブルには2つの列、つまりIDとタイムスタンプがあるとします。私は1日あたりのアイテムの数を取得しようとしています。ここには私の瞬間があります。postgresの大きなテーブルの日付クエリによるグループ化の高速化

SELECT DATE(timestamp) AS day, COUNT(*) 
    FROM actions 
    WHERE DATE(timestamp) >= '20100101' 
    AND DATE(timestamp) < '20110101' 
GROUP BY day; 

インデックスがない場合、これは私のマシンでは約30秒かかります。ここでは、出力を分析し説明します:

GroupAggregate (cost=675462.78..676813.42 rows=46532 width=8) (actual time=24467.404..32417.643 rows=346 loops=1) 
    -> Sort (cost=675462.78..675680.34 rows=87021 width=8) (actual time=24466.730..29071.438 rows=17321121 loops=1) 
     Sort Key: (date("timestamp")) 
     Sort Method: external merge Disk: 372496kB 
     -> Seq Scan on actions (cost=0.00..667133.11 rows=87021 width=8) (actual time=1.981..12368.186 rows=17321121 loops=1) 
       Filter: ((date("timestamp") >= '2010-01-01'::date) AND (date("timestamp") < '2011-01-01'::date)) 
Total runtime: 32447.762 ms 

私はシーケンシャルスキャンを見ているので、私は、日付集計にインデックスに約50%の速度をカット

CREATE INDEX ON actions (DATE(timestamp)); 

を試してみました。

HashAggregate (cost=796710.64..796716.19 rows=370 width=8) (actual time=17038.503..17038.590 rows=346 loops=1) 
    -> Seq Scan on actions (cost=0.00..710202.27 rows=17301674 width=8) (actual time=1.745..12080.877 rows=17321121 loops=1) 
     Filter: ((date("timestamp") >= '2010-01-01'::date) AND (date("timestamp") < '2011-01-01'::date)) 
Total runtime: 17038.663 ms 

私はこの全体的なクエリ最適化ビジネスに慣れていないため、次に何をすべきか分かりません。どのように私はこのクエリをより速く実行させることができたのか?

--edit--

私は指標の限界を打ってるように見えます。これは、このテーブルで実行される唯一のクエリです(ただし、日付の値は変更されます)。テーブルを分割する方法はありますか?または、すべてのカウント値を持つキャッシュテーブルを作成しますか?または他のオプション?

+2

あなたはここに全体の話をしていますか?第1プランと第2プランの間でメモリ設定を変更したようです。それは私のアドバイスだったでしょう。 ;-) –

+0

メモリの設定は変更されていませんが、スキーマの変更ごとにバキューム分析を行っています。私がそれをするべきかどうかわからないが、それは結果に真剣に影響する。 – zaius

答えて

5

テーブルを分割する方法はありますか?

はい:
http://www.postgresql.org/docs/current/static/ddl-partitioning.html

またはすべてのカウント値をキャッシュテーブルを作成しますか?または他のオプション?

「キャッシュ」テーブルを作成することは確かに可能です。しかし、これは、あなたがその結果をどれくらいの頻度で必要とし、どの程度正確である必要があるかによって決まります。

 
CREATE TABLE action_report 
AS 
SELECT DATE(timestamp) AS day, COUNT(*) 
    FROM actions 
    WHERE DATE(timestamp) >= '20100101' 
    AND DATE(timestamp) < '20110101' 
GROUP BY day; 

その後SELECT * FROM action_reportは、あなたがタイムリーに何をしたいあなたを与えるだろう。その後、定期的にそのテーブルを再作成するようにcronジョブをスケジュールします。

このアプローチは、すべてのクエリで時間範囲が変更された場合や、そのクエリが1日に1回しか実行されない場合には役に立ちません。

+0

ああいいね。キャッシュテーブルを作成することで、問題は確実に解決されます。そのテーブルから選択すると(明らかに)超高速です。 1日に古いデータを保存しても問題ありません。また、1日に20秒の更新も問題ありません。最新の週ごとにテーブルを更新する方法はありますか?毎回削除したり再作成するのではなく、 – zaius

+1

毎回作成する必要はありません。一度作成すると、 'TRUNCATE action_report'に続いて' INSERT INTO action_report SELECT .... 'が実行されます。 –

+0

素晴らしい!助けてくれてありがとう。 – zaius

1

範囲のカバーが利用可能なすべてのデータをカバーしているようです。

これは設計上の問題です。これを頻繁に実行する場合は、日付のみを含む追加のtimestamp_date列を作成する方がよいでしょう。次に、その列に索引を作成し、それに応じて照会を変更します。列は、insert + updateトリガーによって維持される必要があります。

SELECT timestamp_date AS day, COUNT(*) 
FROM actions 
WHERE timestamp_date >= '20100101' 
    AND timestamp_date < '20110101' 
GROUP BY day; 

私は行数についての日付範囲があります(それが唯一の小さなサブセットである)間違っている場合は、あなただけにWHERE句を適用し、ちょうどタイムスタンプ列自体にインデックスを試すことができますコラム

SELECT DATE(timestamp) AS day, COUNT(*) 
FROM actions 
WHERE timestamp >= '20100101' 
    AND timestamp < '20110101' 
GROUP BY day; 
+0

はい、フィルタはデータの大きなサブセット用です。より小さなサブセットのクエリは一般的に十分高速です。それは遅い大きなクエリだけです。 timestamp_date列とインデックスを追加しようとしました。残念なことに、どちらのオプションも大幅なスピードアップはありません。出力は次のとおりです:http://pastie.org/1454799 – zaius

+2

なぜそれが違いますか? 'date(timestamp)'のインデックスは 'timestamp_date'カラムを伴うクエリで' timestamp_date'のインデックスと同じ効果を持ちます。 –

+1

@Peter Eisentrautあなたはdownvoteをキャストしておく必要があります。あなたが何を考えているかに関わらず、ザイウスのテストの実際の結果をチェックしてください。少し速く出てきました。すなわち、10%が依然として限界的で重要ではない場合。 – RichardTheKiwi

0

が集約は一時ファイルを使用しているかどうかを確認するためにexplain analyze verbose ...を実行してみてください(範囲を与えられただけでも同様に動作します)。おそらくメモリ内でもっと多くのことができるようにするためには、work_memを増やすでしょうか?

+0

ここにverboseからの出力があります:http://pastie.org/1455439 これはあまり変わっていません。 (プロダクションDBから更新したため、データを追加したことに注意してください) – zaius

+0

どのバージョンのpostgresqlを使用していますか? – araqnid

+0

私はdev環境でこれらのテストをすべて実行しています。これはMacのpostgres 9.0.1です。 Ubuntuでは8.3で動作します(パフォーマンスが実際にカウントされる場所)。私は必要に応じて生産をアップグレードすることができます。 – zaius

2

一般に、返される予想行数が多い場合、ほとんどのデータベースはインデックスを無視します。これは、インデックスヒットごとに行を検索する必要があるため、完全なテーブルスキャンを行うほうが速いからです。この数は10,000〜100,000です。これを実験するには、日付範囲を縮小し、ポストグルがインデックスを使ってどこにフリップするかを見てください。この場合、postgresは17,301,674行をスキャンする予定であるため、テーブルがかなり大きくなります。あなたがそれを本当に小さくしても、ポストグルが間違った選択をしているように感じるなら、ポストグルがその近似を正しく得るようにテーブル上で分析を実行してみてください。

-1

work_memと設定して、計画が変更されるかどうかを確認してください。そうでない場合は、オプションから外れる可能性があります。

0

このようなDSSタイプのクエリに本当に必要なのは、日を表す日付表です。データベース設計用語では、日付次元と呼ばれています。このテーブルにデータを入力するには、この記事で投稿したコードを使用します。http://www.mockbites.com/articles/tech/data_mart_temporal

次に、アクションテーブルの各行に適切なdate_keyを入力します。

あなたのクエリは、次のようになります。キーとfull_dateにインデックスを仮定し

SELECT 
    d.full_date, COUNT(*) 
FROM actions a 
JOIN date_dimension d 
    ON a.date_key = d.date_key 
WHERE d.full_date = '2010/01/01' 
GROUP BY d.full_date 

が、これはINT4キーで動作するため、超高速になります!

もう1つの利点は、他のdate_dimension列でスライスしたりダイスすることができることです。

関連する問題