2016-04-17 108 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); 

這對於那些我們從來沒有從同一個調制解調器兩個讀數相同的時間戳,因此唯一索引系統的完整性重要。

我們目前的挑戰是找到插入讀數的高性能方式。在我們引入歷史數據時,我們經常需要插入數百萬行,而當增加到現有基數1億以上的讀數時,這可能會變得緩慢。

我們目前的方法是將批量10,000讀數導入到temporary_readings表格中,該表格基本上是未讀索引的副本。然後,我們運行下面的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; 

這種運作良好,但需要每〜10,000行的塊20秒內插入。我的問題是雙重的:

  1. 這是解決問題的最佳方法嗎?對於有這些性能要求的項目,我相對較新,所以我很想知道是否有更好的解決方案。
  2. 我可以採取哪些步驟來加速插入過程?

在此先感謝!

+0

您能更詳細地描述您的使用案例嗎?您是否需要實時重複讀取數據,或者您是否正在構建分析倉庫? – wrschneider

+0

'temporary_readings'表是否有任何結構或約束(例如PK或UNIQUE約束)? – wildplasser

回答

3

您的查詢主意是好的。我會嘗試在批處理中計算100,000行,以開始瞭解最佳批處理大小。

但是,distinct on正在放慢速度。這裏有兩個想法。

第一種是假設分批重複的情況非常少見。如果這是真的,請嘗試插入沒有distinct on的數據。如果失敗,請使用distinct on再次運行代碼。這使插入邏輯複雜化,但它可能會使平均插入更短。

第二個是在temporary_readings(timestamp, modem_serial)(不是唯一索引)上建立索引。 Postgres將利用這個索引來實現插入邏輯 - 有時建立一個索引並使用它比其他的執行計劃更快。如果這確實起作用,您可以嘗試更大的批量。

還有第三個解決方案是使用on conflict。這將允許插入本身忽略重複的值。不過,這僅在Postgres 9.5中可用。

+0

謝謝戈登!偉大的建議 - 我會做一些實驗,刪除獨特的子句和添加索引,如你所建議的。將回報,讓你知道我是如何去的:-) –

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); 

此刻你有三個索引,但它們在同一組列上。你不能用唯一的索引來管理嗎?我不知道你的其他查詢是什麼樣的,但你的WHERE NOT EXISTS查詢可以使用這個唯一的索引。

如果您有使用WHERE子句的查詢僅在modem_serial字段上進行篩選。您的獨特索引不太可能被使用。但是,如果您翻轉該索引中的列,它將會是!

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

要從manual引用:

一個多字段的B-tree索引可以與查詢條件 涉及索引中的列的任何子集可以使用,但該指數是最 有效時,有是領先的(最左邊的) 列的限制。