在关于填充数据库的 PostgreSQL 文档中,他们提到了我们如何通过禁用约束和索引来改进批量加载操作,但是看到 INSERT ... ON CONFLICT (upsert) 需要使用唯一约束才能工作,这完全有道理,我一直想知道以下策略有多好:
创建多个连接以将数据批量并行加载到暂存未记录表中
创建延迟约束以对表的数据进行完整性验证
将暂存数据更新插入到一个大目标表 (5GB+) 中,该表还需要具有 PK、唯一索引或唯一约束才能使更新插入成为可能。
众所周知,冲突插入方法比手动创建 SQL 函数或脚本来连接两个表以发现要插入的“新记录”和要更新的“公共记录”要好。这就是为什么 INSERT ... ON CONFLICT 存在的全部意义。
然而,我仍然想知道加载数据后读取数据的性能影响。
问题:
就索引膨胀而言,upsert 会比简单的批量复制插入 + 更新到没有约束的表更糟糕吗?upsert 不利于索引维护吗?
如果是这种情况,我相信更快的加载也意味着加载表后查询性能不佳。我应该在 INSERT ... ON CONFLICT 之后重新创建索引吗?
与 INSERT ... ON CONFLICT 相比,执行此操作的函数/脚本方式虽然对负载的性能较低,但对于索引维护会更好,因为目标表不需要索引来进行更新插入?
我认为情况并非如此。我认为很重要的一点是方便,因此人们不必为单行插入时违反唯一约束而实现捕获和重试循环。由于一些违规行为而不回滚整个批量插入也是一个很好的改进,但我认为这不是重点。而且,如果您无论如何都要使表格脱机以供其他用途,那么我认为这实际上与 ON CONFLICT无关。
ON CONFLICT 付出了高昂的代价来处理细粒度级别的并发。如果你能保证只有这个过程插入新记录,那么一个查询插入新键,第二个查询更新旧键几乎肯定会比使用 ON CONFLICT 更高效。如果您的第 3 点意味着除了支持 ON CONFLICT 所需的约束和索引之外,大表的所有约束和索引都已删除,那么您似乎处于维护窗口中,因此您可能可以保证没有并发插入。
如果您以任何一种方式保持唯一约束,我不希望这是一般情况。但这取决于诸如插入的行的唯一键值是主要按顺序出现还是随机出现的。此外,膨胀将取决于采用 UPDATE 路径的行是否有空间容纳旧版本最初所在的同一块中的更新行(因此是表填充因子),但如果 UPDATE 是单独的语句,这也是正确的. 您必须模拟与您的实际情况相似的东西并对其进行测试以获得明确的答案。
如果您将表脱机以供正常使用并删除所有约束和索引,那么一旦您重建它们,它应该既具有更高的性能,又会导致索引不那么臃肿(尽管可能只是稍微不那么臃肿)。无论您将其与在线使用 ON CONFLICT 进行比较,还是将其与放弃所有但一个约束并离线运行 ON CONFLICT 进行比较,情况都是如此。