【问题标题】:What is the best approach for upserting large number of rows into a single table?将大量行插入单个表的最佳方法是什么?
【发布时间】:2022-02-09 17:12:45
【问题描述】:

我正在开发一种产品,该产品涉及到单个表中的大量 upsert 操作。

我们正在处理基于时间的数据并使用具有 7 天块间隔大小的 timescaledb 超表。我们有并发任务,将数据更新插入到一个表中,在极端情况下,我们可能会有 40 个并发任务,每个更新插入大约 25 万行,都到同一个表中。

最初我们决定采用删除所有旧行的方法,然后使用 COPY FROM 语句插入更新的行,但是当我们要大规模测试系统时,这些 COPYs 需要很长时间才能完成完成,最终导致 db 的 CPU 使用率达到 100%,并且变得无响应。 我们还注意到,表的索引大小急剧增加,磁盘使用率达到 100%,SELECT 语句的执行时间非常长(超过 10 分钟)。我们得出结论,原因是大量删除语句导致索引碎片,并决定采用另一种方法。

根据this post 上的答案,我们决定将所有数据复制到临时表中,然后使用“扩展插入”语句将所有数据插入到实际表中 -

INSERT INTO table SELECT * FROM temp_table ON CONFLICT DO UPDATE...; 

我们的测试表明,它有助于解决索引碎片问题,但大约 250K 的大型 upsert 操作仍然需要 4 分钟以上才能执行,并且在此 upsert 过程中,SELECT 语句需要很长时间才能完成,这对我们来说是不可接受的。

我想知道创建此 upsert 操作的最佳方法是什么,对SELECTs 的性能影响尽可能低。现在唯一想到的是将插入分割成更小的块 -

INSERT INTO table SELECT * FROM temp_table LIMIT 50000 OFFSET 0 ON CONFLICT DO UPDATE ...;
INSERT INTO table SELECT * FROM temp_table LIMIT 50000 OFFSET 50000 ON CONFLICT DO UPDATE ...;
INSERT INTO table SELECT * FROM temp_table LIMIT 50000 OFFSET 100000 ON CONFLICT DO UPDATE ...;
...

但是如果我们批量插入,首先将所有数据复制到临时表中是否有任何好处?它会比简单的多行插入语句执行得更好吗? 以及如何确定拆分 upsert 时使用的最佳块大小是多少?是 使用临时表并直接从中插入行允许更大的块大小?

有没有更好的方法来实现这一点?任何建议将不胜感激

【问题讨论】:

  • 执行插入的单个线程已经在您的系统上施加了足够的负载,以至于您的查询受到影响?也许您需要一个更强大的系统。
  • 没有单线程,40个不同的并发任务(进程),每次插入25万行
  • 好吧,那么解决方案很简单:如果您想限制负载,请使用更少的线程。
  • 我不想限制负载:) 我想优化它.. 找到插入尽可能多行的最佳方法
  • 优化插入的最佳方法是告诉您的选择去跳湖。您可以决定哪一个不那么令人反感。

标签: postgresql performance upsert timescaledb large-scale


【解决方案1】:

您可以采取一些措施来加快数据加载速度:

  • 加载数据时表上没有索引或外键(检查约束很好)。我并不是建议您删除所有约束,但您可以例如使用分区,将数据加载到新表中,然后创建索引和约束并将表附加为新分区。

  • 使用COPY 加载数据。如果您不能使用COPY,请为INSERT 使用准备好的语句以节省解析时间。

  • 在单个事务中加载许多行。

  • max_wal_size 设置为高,这样您就不会获得不必要的检查点。

  • 获取快速的本地磁盘。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-12-07
    • 2010-09-22
    • 2012-03-07
    • 1970-01-01
    • 1970-01-01
    • 2019-05-28
    • 2015-10-16
    • 1970-01-01
    相关资源
    最近更新 更多