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


10

私は若い開発者であり、データベース(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億行まであり、クエリには非常に長い時間がかかります。私のselectステートメントは常にクエリを実行し、多くの場合はtime + macもクエリを実行するため、私はtimestampandと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

回答:


1

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

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

それでも解決しない場合は、インデックスを付けるフィールドと同じフィールドで分割するか、timestampまたはmacです。

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


2
Postgresにはクラスター化インデックスがありません(インデックスに沿ってテーブルをクラスター化することはできますが、手動で実行する必要があり、「とどまる」ことはありません)
a_horse_with_no_name

アドバイスありがとう。現在は以前よりも高速で実行されていますが、クエリあたり30秒を超える非常に低いパフォーマンスのままです。私もクラスタリングを行いましたが、@ a_horse_with_no_nameが言ったように:postgresではこれはワンショットです。私のデータ型は正しいと思います。私はそれらを質問に追加しました
manman

クラスタ化されたテーブルがない場合、範囲クエリに対する次の推奨事項はパーティション化です。
Jon of All Trades

-2

私は、電気メーターから何十億もの測定値があり、ほとんどのクエリを10秒未満で実行するアプリケーションに取り組みました。

私たちの環境は異なっていました。サーバークラスマシン上のMicrosoft SQL Server(4コア、24 GBメモリ)。サーバーにアップグレードする機会はありますか?

大きな問題の1つは、読み取り値を1つずつ取り込むと、データベースのパフォーマンスに大きな影響を与えることです。データの書き込みに必要なロックとクエリは待機します。バッチで挿入できますか?

スキーマを使用すると、4つの非常に大きなテーブルが作成されます。すべての結合で両方のテーブルのインデックスを使用することが重要です。テーブルスキャンには時間がかかります。null可能なフィールドを持つ1つのテーブルにそれらをマージすることは可能ですか?


バッチでの挿入:一括挿入を実行できますが、現時点では、クエリの実行中に挿入がまったく行われないテストデータベースで作業しています。しかし、後で考えます。 :) インデックス:私はすべてのテーブルにインデックスを持っています。データテーブルのIDのインデックス、(mac、タイムスタンプ)のdata_blockテーブルのインデックス。左結合ごとにdataAを検索しているときにも問題がありますが、そうではありません。インデックスがあっても、データテーブルを検索します。 null可能フィールド: data_blockは1種類のデータを複数持つことができるため、不可能です。1xdata_block - > 4xdataA例えば
MANMAN

DBツールはクエリアナライザを提供しますか?idに基づくdata_blockのインデックスが必要になる場合があります。
KC-NH

やってみますが、なぜこれが役立つのかわかりません!?
マンマン2014年

-2

Postgres(またはその他のRDBMS)に固有のスケーラビリティーの限界に達しています。

RDBMSインデックスはBツリーであることを忘れないでください。Bツリーは、平均と最悪の両方のケースでO(log n)です。これにより、Nの妥当な値に対して、安全で予測可能な優れた選択肢になります。Nが大きくなりすぎると、機能が停止します。

NoSQLデータベースは(ほとんどの場合)ハッシュテーブルです。ハッシュテーブルは、平均的な場合はO(1)、最悪の場合はO(n)です。最悪のケースを回避できると仮定すると、Nの非常に大きな値に対して非常に効果的です。

さらに、ハッシュテーブルは並列化が簡単ですが、Bツリーはそうではありません。これにより、ハッシュテーブルは分散コンピューティングアーキテクチャにより適しています。

10億行のテーブルを取得し始めたら、RDBMSからNoSQLへの切り替えを検討するときがきました。Cassandraは、おそらくユースケースに適しています。


2
多くのRDBMSには、Bツリーインデックス(ハッシュ、ビットマップなど)よりも多くのオプションがあります。一部のDBMSは行を格納し、一部のDBMSは列を格納しています。そして、O(logn)は、数十億行であっても悪くありません。また、4GBのメモリマシンを使用している場合は、制限に達することはありません。
ypercubeᵀᴹ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.