2016-04-17 17 views
1

現在、フィールドにワイヤレスモデムのネットワークから大量のデータを収集するプロジェクトに取り組んでいます。Postgresの大きなテーブルを重複してチェックする効率的な挿入

CREATE TABLE public.readings (
    id INTEGER PRIMARY KEY NOT NULL DEFAULT nextval('readings_id_seq'::regclass), 
    created TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now(), 
    timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL, 
    modem_serial CHARACTER VARYING(255) NOT NULL, 
    channel1 INTEGER NOT NULL, 
    channel2 INTEGER NOT NULL, 
    signal_strength INTEGER, 
    battery INTEGER, 
    excluded BOOLEAN NOT NULL DEFAULT false 
); 
CREATE UNIQUE INDEX _timestamp_modemserial_uc ON readings USING BTREE (timestamp, modem_serial); 
CREATE INDEX ix_readings_timestamp ON readings USING BTREE (timestamp); 
CREATE INDEX ix_readings_modem_serial ON readings USING BTREE (modem_serial); 

それは我々が同じタイムスタンプと同じモデムから2つの測定値、したがって、一意のインデックスを持っていることはありませんシステムの整合性のために重要です。私たちはこのようになりますテーブル「測定値」を持っています。

現時点で私たちの課題は、読書を挿入する演奏方法を見つけることです。過去のデータを取り込む際には、何百万行も挿入する必要があります。既存のデータに1億分の1を加えた値を加算すると、これはかなり遅くなります。

私たちの現在のアプローチは、10,000回の読み込みのバッチを一時的な読み込みテーブルにインポートすることです。これは、本質的に読み取りのコピーされていないコピーです。私たちは、その後、メインテーブルにそれをマージし、重複を削除するには、以下のSQLを実行します。

INSERT INTO readings (created, timestamp, modem_serial, channel1, channel2, signal_strength, battery) 
SELECT DISTINCT ON (timestamp, modem_serial) created, timestamp, modem_serial, channel1, channel2, signal_strength, battery 
FROM temporary_readings 
WHERE NOT EXISTS(
    SELECT * FROM readings 
    WHERE timestamp=temporary_readings.timestamp 
    AND modem_serial=temporary_readings.modem_serial 
) 
ORDER BY timestamp, modem_serial ASC; 

これはうまく動作しますが、挿入するために〜万行ブロックごとに20秒かかります。私の質問は二重です:

  1. これは問題に近づく最も良い方法ですか?私はこれらの種類のパフォーマンス要求を持つプロジェクトには比較的新しいので、より良いソリューションがあるかどうかを知りたいと思っています。
  2. 挿入プロセスのスピードアップにはどのような手順が必要ですか?事前に

ありがとう!

+0

あなたのユースケースについて詳しく説明できますか?リアルタイムで読み上げをデデュープする必要があるのですか、アナリティクス用のウェアハウスを構築していますか? – wrschneider

+0

'temporary_readings'テーブルに構造や制約(PKやUNIQUE制約など)がありますか? – wildplasser

答えて

3

質問の候補は大丈夫です。最適なバッチサイズのアイデアを得るために、バッチ内で100,000行のタイミングをとることを試みます。

ただし、distinct onは処理が遅くなっています。ここに2つのアイデアがあります。

最初は、バッチ内の複製が非常にまれであると仮定することです。これが当てはまる場合は、distinct onのないデータを挿入してみてください。それが失敗した場合は、distinct onでコードを再度実行してください。これは挿入ロジックを複雑にしますが、平均挿入を大幅に短縮する可能性があります。

temporary_readings(timestamp, modem_serial)(一意のインデックスではありません)にインデックスを作成します。 Postgresはこのインデックスを挿入ロジックに利用します。インデックスを作成して使用すると、代替実行プランよりも高速です。これがうまくいけば、より大きなバッチサイズを試すことができます。

on conflictを使用する3つ目の解決方法があります。これにより、挿入自体が重複する値を無視できるようになります。これはPostgreSQL 9.5でのみ利用可能です。

+0

ありがとうゴードン!素晴らしい提案 - 私は別のon節を削除し、あなたが示唆しているようにインデックスを追加する実験をいくつか行います。私がどうやって行くのかを知らせるために報告します:-) –

1

すでにインデックスが1億個含まれているテーブルに追加すると、インデックスを少し見直していくと、おそらく何が速くなるかに関わらず、処理速度が遅くなります。

CREATE UNIQUE INDEX _timestamp_modemserial_uc ON readings USING BTREE (timestamp, modem_serial); 
CREATE INDEX ix_readings_timestamp ON readings USING BTREE (timestamp); 
CREATE INDEX ix_readings_modem_serial ON readings USING BTREE (modem_serial); 

現在、3つのインデックスがありますが、それらは同じ列のセットにあります。ユニークなインデックスだけで管理することはできませんか?私はあなたの他のクエリがどのようなものか分かりませんが、WHERE NOT EXISTSクエリはこのユニークなインデックスを利用できます。

WHERE句では、modem_serialフィールドでのみフィルタリングするクエリがある場合。一意のインデックスは使用されません。ただし、そのインデックスの列を反転させると、それが表示されます。

複数列B-treeインデックスは、インデックスの列の任意のサブセットを伴う 問い合わせ条件で使用することができますが、インデックスは効率的で最も ときがあります:manualから引用し

CREATE UNIQUE INDEX _timestamp_modemserial_uc ON readings USING BTREE (timestamp, modem_serial); 

先頭の(一番左の) 列の制約です。

関連する問題