【发布时间】: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