3

環境

netflow データ (ルーターによって傍受されたすべてのパケット) を保持するテーブルがあります。このテーブルには、現時点で約 590 万行あります。

問題

1 日に受信したパケット数をカウントする簡単なクエリを試していますが、それほど時間はかかりません。

初めて実行したとき、クエリには88 秒かかり、2 回目の実行後は33 秒、その後のすべての実行で5 秒かかりました。

主な問題はクエリの速度ではなく、同じクエリを 3 回実行した後、速度が 20 倍近く速くなることです。query cache
の概念は理解していますが、元のクエリ実行のパフォーマンスは意味がありません。

テスト

結合に使用している列 (datetime) はタイプtimestamptzであり、インデックスが付けられています。

CREATE INDEX date ON netflows USING btree (datetime);

EXPLAINステートメントを見てください。実行の違いはNested Loop.

VACUUM ANALYZEまったく同じ結果のテーブルが既にあります。

現在の環境

  • VMware ESX 4.1 で実行されている Linux Ubuntu 12.04 VM
  • PostgreSQL 9.1
  • VM には 2 GB RAM、2 コアがあります。
  • データベースサーバーはこれに完全に専念しており、他に何もしていません
  • 毎分テーブルに挿入します (毎分 100 行)
  • 非常に低いディスク、RAM、または CPU アクティビティ

クエリ

with date_list as (
    select
        series as start_date,
        series + '23:59:59' as end_date
    from
        generate_series(
            (select min(datetime) from netflows)::date, 
            (select max(datetime) from netflows)::date, 
            '1 day') as series
)
select
    start_date,
    end_date,
    count(*)
from
    netflows
    inner join date_list on (datetime between start_date and end_date)
group by
    start_date,
    end_date;

最初の実行の説明 (88 秒)

Sort  (cost=27007355.59..27007356.09 rows=200 width=8) (actual time=89647.054..89647.055 rows=18 loops=1) 
  Sort Key: date_list.start_date 
  Sort Method: quicksort  Memory: 25kB 
  CTE date_list 
    ->  Function Scan on generate_series series  (cost=0.13..12.63 rows=1000 width=8) (actual time=92.567..92.667 rows=19 loops=1) 
          InitPlan 2 (returns $1) 
            ->  Result  (cost=0.05..0.06 rows=1 width=0) (actual time=71.270..71.270 rows=1 loops=1) 
                  InitPlan 1 (returns $0) 
                    ->  Limit  (cost=0.00..0.05 rows=1 width=8) (actual time=71.259..71.261 rows=1 loops=1) 
                          ->  Index Scan using date on netflows  (cost=0.00..303662.15 rows=5945591 width=8) (actual time=71.252..71.252 rows=1 loops=1) 
                                Index Cond: (datetime IS NOT NULL) 
          InitPlan 4 (returns $3) 
            ->  Result  (cost=0.05..0.06 rows=1 width=0) (actual time=11.786..11.787 rows=1 loops=1) 
                  InitPlan 3 (returns $2) 
                    ->  Limit  (cost=0.00..0.05 rows=1 width=8) (actual time=11.778..11.779 rows=1 loops=1) 
                          ->  Index Scan Backward using date on netflows  (cost=0.00..303662.15 rows=5945591 width=8) (actual time=11.776..11.776 rows=1 loops=1) 
                                Index Cond: (datetime IS NOT NULL) 
  ->  HashAggregate  (cost=27007333.31..27007335.31 rows=200 width=8) (actual time=89639.167..89639.179 rows=18 loops=1) 
        ->  Nested Loop  (cost=0.00..23704227.20 rows=660621222 width=8) (actual time=92.667..88059.576 rows=5945457 loops=1) 
              ->  CTE Scan on date_list  (cost=0.00..20.00 rows=1000 width=16) (actual time=92.578..92.785 rows=19 loops=1) 
              ->  Index Scan using date on netflows  (cost=0.00..13794.89 rows=660621 width=8) (actual time=2.438..4571.884 rows=312919 loops=19) 
                    Index Cond: ((datetime >= date_list.start_date) AND (datetime <= date_list.end_date)) 
Total runtime: 89668.047 ms 

3 回目の実行の EXPLAIN (5 秒)

Sort  (cost=27011357.45..27011357.95 rows=200 width=8) (actual time=5645.031..5645.032 rows=18 loops=1) 
  Sort Key: date_list.start_date 
  Sort Method: quicksort  Memory: 25kB 
  CTE date_list 
    ->  Function Scan on generate_series series  (cost=0.13..12.63 rows=1000 width=8) (actual time=0.108..0.204 rows=19 loops=1) 
          InitPlan 2 (returns $1) 
            ->  Result  (cost=0.05..0.06 rows=1 width=0) (actual time=0.050..0.050 rows=1 loops=1) 
                  InitPlan 1 (returns $0) 
                    ->  Limit  (cost=0.00..0.05 rows=1 width=8) (actual time=0.046..0.046 rows=1 loops=1) 
                          ->  Index Scan using date on netflows  (cost=0.00..303705.14 rows=5946469 width=8) (actual time=0.046..0.046 rows=1 loops=1) 
                                Index Cond: (datetime IS NOT NULL) 
          InitPlan 4 (returns $3) 
            ->  Result  (cost=0.05..0.06 rows=1 width=0) (actual time=0.026..0.026 rows=1 loops=1) 
                  InitPlan 3 (returns $2) 
                    ->  Limit  (cost=0.00..0.05 rows=1 width=8) (actual time=0.026..0.026 rows=1 loops=1) 
                          ->  Index Scan Backward using date on netflows  (cost=0.00..303705.14 rows=5946469 width=8) (actual time=0.026..0.026 rows=1 loops=1) 
                                Index Cond: (datetime IS NOT NULL) 
  ->  HashAggregate  (cost=27011335.17..27011337.17 rows=200 width=8) (actual time=5645.005..5645.009 rows=18 loops=1) 
        ->  Nested Loop  (cost=0.00..23707741.28 rows=660718778 width=8) (actual time=0.134..4176.406 rows=5946329 loops=1) 
              ->  CTE Scan on date_list  (cost=0.00..20.00 rows=1000 width=16) (actual time=0.110..0.343 rows=19 loops=1) 
              ->  Index Scan using date on netflows  (cost=0.00..13796.94 rows=660719 width=8) (actual time=0.026..164.117 rows=312965 loops=19) 
                    Index Cond: ((datetime >= date_list.start_date) AND (datetime <= date_list.end_date)) 
Total runtime: 5645.189 ms
4

2 に答える 2

3

あなたがやっているINNER JOINなら、CTEはまったく必要ないと思います。定義できます

select
    datetime::date,
    count(*)
from netflows
group by datetime::date /* or GROUP BY 1 as Postgres extension */

LEFT JOIN必要に応じてゼロを取得したい場合を除き、日付テーブルが必要な理由がわかりません。これは、データを 1 回通過することを意味します。

ところで、エンティティと列に date や datetime などのキーワードを使用しないことをお勧めします。たとえそれが合法であっても、それだけの価値はありません。

于 2012-12-06T00:57:12.237 に答える
1
WITH date_list as (
    SELECT t                  AS start_date
         ,(t + interval '1d') AS end_date
    FROM  (
      SELECT generate_series((min(datetime))::date
                            ,(max(datetime))::date
                            ,'1d') AS t
      FROM   netflows
      ) x
   )
SELECT d.start_date
      ,count(*) AS ct
FROM   date_list     d
LEFT   JOIN netflows n ON n.datetime >= d.start_date
                      AND n.datetime <  d.end_date
GROUP  BY d.start_date;

そして、インデックスに適切な名前を使用してください(すでに@Andrewによって示唆されています):

CREATE INDEX netflows_date_idx ON netflows (datetime);

主なポイント

  • @Andrewが彼の答えですでに述べたように、カレンダーの毎日の行が必要だと仮定すると、 を に置き換えましJOINLEFT JOIN

  • 1 回のクエリでネットフローを取得min()して取得する方がはるかに効率的です。max()

  • 簡易型キャスト。

  • 日付範囲を修正しました。のようなタイムスタンプの場合、コードは失敗し'2012-12-06 23:59:59.123'ます。

これを大きなテーブルでテストしたところ、パフォーマンスは良好でした。
あなたの元の質問について:特にRAMが限られている場合、間違いなく予想される効果をキャッシュします。

于 2012-12-06T11:53:24.727 に答える