web-dev-qa-db-ja.com

billions-rows-tableでの遅いクエリ//使用されたインデックス

私は若い開発者であり、データベース(PostgreSQL 9.3)の使用にあまり熟練していないので、本当に助けが必要なプロジェクトでいくつかの問題に遭遇しました。

私のプロジェクトは、デバイス(最大1000以上のデバイス)からデータを収集することです。ここで、すべてのデバイスが毎秒1つのデータブロックを送信しており、1時間あたり約300万行になります。

現在、すべてのデバイスの受信データを格納する1つの大きなテーブルがあります。

CREATE TABLE data_block(
    id bigserial
    timestamp timestamp
    mac bigint
)

データブロックに含めることができる(またはできない)データにはいくつかの種類があるため、data_blockテーブルを参照する他のテーブルがあります。

CREATE TABLE dataA(
    data_block_id bigserial
    data

    CONSTRAINT fkey FOREIGN KEY (data_block_id) REFERENCES data_block(id);
);
CREATE TABLE dataB(...);
CREATE TABLE dataC(...);
CREATE INDEX index_dataA_block_id ON dataA (data_block_id DESC);
...

1つのdata_blockに3x dataA、1x dataBがあるが、dataCがない可能性があります。

データは数週間保持されるので、このテーブルには約50億行が含まれます。現時点では、テーブルに6億行まであり、クエリには非常に長い時間がかかります。そのため、timestampmacのインデックスを作成することにしました。これは、selectステートメントが常にクエリを実行し、多くの場合、time + macもクエリを実行するためです。

CREATE INDEX index_ts_mac ON data_block (timestamp DESC, mac);

...しかし、クエリにはまだ時間がかかります。たとえば、1日と1つのMacのデータをクエリしました。

SELECT * FROM data_block 
WHERE timestamp>'2014-09-15' 
AND timestamp<'2014-09-17' 
AND mac=123456789
Index Scan using index_ts_mac on data_block  (cost=0.57..957307.24 rows=315409 width=32) (actual time=39.849..334534.972 rows=285857 loops=1)
  Index Cond: ((timestamp > '2014-09-14 00:00:00'::timestamp without time zone) AND (timestamp < '2014-09-16 00:00:00'::timestamp without time zone) AND (mac = 123456789))
Total runtime: 334642.078 ms

クエリを実行する前に完全なバキュームを行いました。大きなテーブルでこのような問題を解決して10秒未満のクエリを実行するエレガントな方法はありますか?

パーティション分割について読みましたが、dataA、dataB、dataCがdata_block_idを参照している場合は機能しませんか?それが何らかの方法で機能する場合、時間をかけて、またはMacでパーティションを作成する必要がありますか?

インデックスを別の方向に変更しました。最初のMAC、次にタイムスタンプ、そしてそれは多くのパフォーマンスを獲得します。

CREATE INDEX index_mac_ts ON data_block (mac, timestamp DESC);

ただし、クエリには30秒以上かかります。特に、データテーブルでLEFT JOINを実行する場合は特にそうです。以下は、新しいインデックスを使用したクエリのEXPLAIN ANALYZEです。

EXPLAIN ANALYZE SELECT * FROM data_block WHERE mac = 123456789 AND timestamp < '2014-10-05 00:00:00' AND timestamp > '2014-10-04 00:00:00'
Bitmap Heap Scan on data_block  (cost=1514.57..89137.07 rows=58667 width=28) (actual time=2420.842..32353.678 rows=51342 loops=1)
  Recheck Cond: ((mac = 123456789) AND (timestamp < '2014-10-05 00:00:00'::timestamp without time zone) AND (timestamp > '2014-10-04 00:00:00'::timestamp without time zone))
  ->  Bitmap Index Scan on index_mac_ts  (cost=0.00..1499.90 rows=58667 width=0) (actual time=2399.291..2399.291 rows=51342 loops=1)
        Index Cond: ((mac = 123456789) AND (timestamp < '2014-10-05 00:00:00'::timestamp without time zone) AND (timestamp > '2014-10-04 00:00:00'::timestamp without time zone))
Total runtime: 32360.620 ms 

残念ながら、私のハードウェアは厳しく制限されています。 Intel i3-2100 @ 3.10Ghz、4GB RAMを使用しています。私の現在の設定は次のとおりです:

default_statistics_target = 100
maintenance_work_mem = 512MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 4GB
work_mem = 512MB
wal_buffers = 16MB
checkpoint_segments = 32
shared_buffers = 2GB
max_connections = 20
random_page_cost = 2
10
manman

これは私のMS SQLバイアスを反映しているかもしれませんが、timestampでテーブルをクラスター化してみます。特定の期間にわたって頻繁にデータをプルしている場合、データが物理的に隣接して保存されるため、これは役立ちます。システムは、開始点までシークし、範囲の最後までスキャンして、完了することができます。特定の時間をクエリする場合、それは3,600,000レコードにすぎません。

クエリ(つまり...?)が特定のマシンに対するものである場合、Postgresはこれらの3.6 Mレコードの99.9%を除外する必要があります。この1000分の1フィルターが一般的な日付範囲フィルターよりも選択的である場合は、より選択的なmacフィールドをインデックスの最初のコンポーネントとして使用する必要があります。それでもクラスタリングする価値があるかもしれません。

それでもそれができない場合は、インデックスを作成しているのと同じフィールド(timestampまたはmac)でパーティション分割します。

データ型を指定しませんでした。それらはデータに適していますか?たとえば、日付をテキストとして保存すると、テーブルが不必要に肥大化します。

1