From PostgreSQL 9.6 发行说明 https://www.postgresql.org/docs/9.6/static/release-9-6.html#AEN130398:
只有通过顺序扫描访问驱动表的严格只读查询才能并行化。
我的问题是:如果 CTE (WITH
子句)仅包含读取操作,但其结果用于提供写入操作,例如插入或更新,是否也不允许并行顺序扫描?
我的意思是,CTE 很像仅针对当前正在执行的查询而存在的临时表,我可以假设它的内部查询可以利用 PostgreSQL 9.6 的全新并行 seq-scan 吗?或者,否则,它是否被视为using子查询不能并行扫描?
例如,考虑以下查询:
WITH foobarbaz AS (
SELECT foo FROM bar
WHERE some_expensive_function(baz)
)
DELETE FROM bar
USING foobarbaz
WHERE bar.foo = foobarbaz.foo
;
foobarbaz 计算是否期望能够并行化,或者是否因为删除语句而被禁止?
如果不允许,我认为可以用创建临时表陈述。但我想我会陷入同样的问题创建表是一个写操作。我错了吗?
最后,我可以尝试的最后一次机会是将其作为纯读取操作执行,并将其结果用作插入和/或更新操作的输入。在事务之外它应该可以工作。但问题是:如果读操作和插入/更新之间是begin
and commit
句子,无论如何都不会被允许吗?我知道它们是两个完全不同的操作,但是在同一个事务和 Postgres 中。
需要明确的是,我担心的是,我有大量难以阅读和难以重新设计的 SQL 查询,这些查询涉及使用低性能函数调用的多次顺序扫描,并对两个表执行复杂的更改。整个过程在单个事务中运行,因为如果不是这样,发生故障时的混乱将完全无法恢复。
我的希望是能够并行化一些顺序扫描,以利用机器的 8 个 cpu 核心,从而能够更快地完成该过程。
请不要回答说我需要完全重新设计这个混乱:我知道并且我正在努力解决它。但这是一个伟大的项目,我们需要在此期间继续努力。
无论如何,任何建议都会感激不尽。
EDIT:
我添加一个简短的报告,说明我目前所能发现的情况:
正如 @a_horse_with_no_name 在他的评论中所说(谢谢),CTE 和查询的其余部分是单个 DML 语句,如果它有写操作,即使在 CTE 之外,那么 CTE 也无法并行化(我也测试了它) 。
我还发现了这个维基页面 https://wiki.postgresql.org/wiki/Parallel_Query有关并行扫描的信息比我在发行说明链接中找到的信息更简洁。
感谢该维基页面,我可以检查的一个有趣的点是我需要将所涉及的函数声明为并行安全。我做到了并工作了(在没有写作的测试中)。
-
另一个有趣的点是 @a_horse_with_no_name 在他的第二条评论中所说的:使用 DbLink 执行纯只读查询。但是,对此进行了一些调查,我发现postgres_fdw https://www.postgresql.org/docs/9.6/static/postgres-fdw.html,在 wiki 中明确提到不支持并行扫描,使用更现代且符合标准的基础设施提供大致相同的功能 https://www.postgresql.org/docs/9.6/static/dblink.html.
- 另一方面,即使它有效,我最终也会从交易外部获取数据,在某些情况下,这对我来说是可以接受的,但我认为,这并不像一般解决方案那么好。
最后,我检查了是否可以在只读查询中执行并行扫描inside一个事务,即使它稍后执行写操作(没有触发异常并且我可以提交)。
...总而言之,我认为我最好的选择(如果不是唯一的选择)是重构脚本,使其先将数据读取到内存,然后再在同一事务中执行写入操作。
它会增加 I/O 开销,但是,考虑到我管理的延迟,效果会更好。