sql - 高效插入,对 Postgres 中的大表进行重复检查

标签 sql performance postgresql sql-insert

我目前正在从事一个项目,从现场的无线调制解调器网络收集大量数据。我们有一个看起来像这样的“读数”表:

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 秒才能插入。我的问题是双重的:

  1. 这是解决问题的最佳方法吗?我对具有此类性能需求的项目还比较陌生,所以我很想知道是否有更好的解决方案。
  2. 我可以采取哪些步骤来加快插入过程?

提前致谢!

最佳答案

你的查询思路没问题。我会尝试将其计时为批处理中的 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/

相关文章:

django - 我们怎样才能使 Django 测试更快?

postgresql - 使用 clojure.java.jdbc/db-do-prepared 检查 uuid 是否存在于 PostgreSQL 表中失败

sql - 如何强制提交正在运行的 t-sql 查询(完成一半)?

c# - 使用 LINQ 查询巨大的 CSV 和 Excel

sql - 如何返回引号内的字符串

javascript - 如何使用 JQuery 高效地 append 内容

sql - MSSQL - 有没有一种方法可以在将条目添加到另一个表时自动将条目添加到一个表?

java - JSF 和 Spring 性能与糟糕的 JSP 性能

java - 慢速双缓冲方法

arrays - 聚合多维数组的函数中的初始数组