5

PostgreSQL 9.6 发行说明

只有通过顺序扫描访问驱动表的严格只读查询才能被并行化。

我的问题是:如果 CTE(WITH子句)仅包含读取操作,但其结果用于提供写入操作,例如插入或更新,是否也不允许并行化顺序扫描?

我的意思是,由于 CTE 很像一个临时表,它只存在于当前执行的查询中,我可以假设它的内部查询可以利用 PostgreSQL 9.6 的全新并行 seq-scan 吗?或者,否则,它是否被视为使用子查询并且无法执行并行扫描?

例如,考虑这个查询:

WITH foobarbaz AS (
  SELECT foo FROM bar
  WHERE some_expensive_function(baz)
)
DELETE FROM bar
USING foobarbaz
WHERE bar.foo = foobarbaz.foo
;

该 foobarbaz 计算是否预计能够并行化,还是因为删除语句而被禁止?

如果不允许,我认为可以用CREATE TEMPORARY TABLE语句替换 CTE。但我想我会遇到与CREATE TABLE是写操作相同的问题。我错了吗?

最后,我可以尝试的最后一次机会是将其作为纯读取操作执行,并将其结果用作插入和/或更新操作的输入。在事务之外它应该工作。但问题是:如果读操作和插入/更新在abegincommit句子之间,无论如何都不允许?我了解它们是两个完全不同的操作,但在同一个事务和 Postgres 中。

需要明确的是,我担心的是我有大量难以阅读和难以重新设计的 SQL 查询,这些查询涉及多次顺序扫描和低性能函数调用,并且对两个表执行复杂的更改。整个过程在单个事务中运行,因为如果没有,发生故障时的混乱将完全无法恢复。

我希望能够并行化一些顺序扫描,以利用机器的 8 个 cpu 内核来更快地完成该过程。

请不要回答我需要完全重新设计那个烂摊子:我知道并且我正在努力。但这是一个伟大的项目,我们需要在此期间继续工作。

无论如何,任何建议将不胜感激。

编辑:

我添加了一份关于到目前为止我可以发现的内容的简短报告:

  • 正如@a_horse_with_no_name 在他的评论中所说(谢谢),CTE 和查询的其余部分是单个 DML 语句,如果它有一个写操作,即使在 CTE 之外,那么 CTE 也不能并行化(我也测试过) .

  • 此外,我发现这个wiki 页面比我在发行说明链接中找到的有关并行扫描的信息更简洁。

  • 多亏了那个 wiki 页面,我可以检查的一个有趣的点是我需要将所涉及的函数声明为并行安全。我做到了并且工作了(在没有写作的测试中)。

  • 另一个有趣的点是@a_horse_with_no_name 在他的第二条评论中所说的:使用 DbLink 执行纯只读查询。但是,对此进行一些调查后,我发现postgres_fdw(在 wiki 中明确提及为不支持并行扫描)使用更现代且符合标准的基础架构提供大致相同的功能

    • 而且,另一方面,即使它会起作用,我最终还是从事务外部获取数据,在某些情况下这对我来说是可以接受的,但我认为这不如一般解决方案好。
  • 最后,我检查了是否可以在事务内的只读查询中执行并行扫描,即使它稍后执行写操作(没有触发异常并且我可以提交)。

...总之,我认为我最好的选择(如果不是唯一的选择)是重构脚本,使其先将数据读取到内存中,然后在同一事务中执行写入操作。

它会增加 I/O 开销,但是,处理我管理的延迟会更好。

4

0 回答 0