我目前正在从事一个项目,从现场的无线调制解调器网络收集大量数据。我们有一个看起来像这样的“读数”表:
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 个读数的批处理导入到临时读数表中,该表本质上是读数的未索引副本。然后我们运行以下 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 行 block 需要大约 20 秒才能插入。我的问题是双重的:
- 这是解决问题的最佳方法吗?我对具有此类性能需求的项目还比较陌生,所以我很想知道是否有更好的解决方案。
- 我可以采取哪些步骤来加快插入过程?
提前致谢!
最佳答案
你的查询思路没问题。我会尝试将其计时为批处理中的 100,000 行,以开始了解最佳批处理大小。
但是,distinct on
正在减慢速度。这里有两个想法。
首先是假设批量重复是非常罕见的。如果这是真的,请尝试插入没有 distinct on
的数据。如果失败,则使用 distinct on
再次运行代码。这会使插入逻辑复杂化,但可能会使平均插入时间更短。
第二种是在temporary_readings(timestamp, modem_serial)
上建立索引(不是唯一索引)。 Postgres 将利用此索引作为插入逻辑——有时构建索引并使用它比其他执行计划更快。如果这确实有效,您可以尝试更大的批量大小。
还有第三种解决方案,即使用on conflict
。这将允许插入本身忽略重复值。不过,这仅在 Postgres 9.5 中可用。
关于sql - 高效插入,对 Postgres 中的大表进行重复检查,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36675809/