PostgreSQL 9.6.0 文档 | |||
---|---|---|---|
Prev | Up | Chapter 15. 并行查询 | Next |
因为每个工作者只执行完成计划的并行部分,所以不可能简单地产生一个普通查询计划并使用多个工作者运行它。每个工作者都会产生输出结果集的一个完全拷贝,因而查询并不会比普通查询运行得更快甚至还会产生不正确的结果。相反,计划的并行部分一定被查询优化器在内部当作一个部分计划。也就是说,一定要这样来创建计划,使得每个将执行该计划的进程只产生输出行的一个子集,这样可以保证每个需要被输出的行刚好会被合作进程产生一次。
当前唯一一种被修改用于并行查询的扫描类型是顺序扫描。因此在并行计划中的驱动表将总是被使用并行顺序扫描进行扫描。关系的块将被划分给合作进程。一次发放一个文件块,这样对于关系的访问仍然保持为顺序访问。在请求一个新页面之前,每一个进程将访问分配给它的页面上的每一个元组。
驱动表将被使用嵌套循环或者哈希连接连接到一个或者多个其他表。在连接的外侧可以是任何一种被规划器支持可以安全地在并行工作者中运行的非并行计划。例如,它可以是一个索引扫描,基于从内表取得的一列来查找值。每个工作者都将会完整地执行外侧的计划,这也是为什么这里不能支持归并连接的原因。归并连接的外侧常常涉及到排序整个内表,即便使用索引,多次在内表上进行完全索引扫描也效率不高。
将查询的聚集部分整个地并行执行是不可能的。例如,如果一个查询涉及到选择COUNT(*),每个工作者可以计算一个总和,但是这些总和需要被整合在一起以产生最终的答案。如果一个计划涉及到GROUP BY子句,为每个组需要计算出一个单独的总和。即使聚集不能完全地并行执行,但是涉及聚集的查询常常是并行查询很好的候选,因为它们通常都是读很多行但只返回少数几行给客户端。返回很多行给客户端的查询常常受制于客户端读取数据的速度,这种情况下并行查询帮助不大。
PostgreSQL通过做两次聚集来支持并行聚集。第一次,每个参与查询计划并行部分执行的进程执行一个聚集步骤,为进程发现的每个分组产生一个部分结果。这在计划中反映为一个PartialAggregate节点。第二次,部分结果被通过Gather节点传输给领导者。最后,领导者对所有工作者的部分结果进行重聚集以得到最终的结果。这在计划中反映为一个FinalizeAggregate节点。
并行聚集并不能支持所有的情况。每个聚集对于并行机制一定要是安全的,并且必须有一个结合函数。如果聚集有一个internal类型的转移状态,它必须有序列化和反序列化函数。详见CREATE AGGREGATE。对于有序集聚集或者查询涉及GROUPING SETS时不支持并行聚集。只有当查询中涉及的所有连接也是计划中并行不分的一部分时,才能使用并行聚集。
如果我们想要一个查询能产生并行计划但事实上又没有产生,可以尝试减小parallel_setup_cost或者parallel_tuple_cost。当然,这个计划可能比规划器优先产生的顺序计划还要慢,但也不总是如此。如果将这些设置为很小的值(例如把它们设置为零)也不能得到并行计划,那就可能是有某种原因导致查询规划器无法为你的查询产生并行计划。可能的原因可见Section 15.2和Section 15.4。
在执行一个并行计划时,可以用EXPLAIN (ANALYZE,VERBOSE)来显示每个计划节点在每个工作者上的统计信息。这些信息有助于确定是否所有的工作被均匀地分发到所有计划节点以及从总体上理解计划的性能特点。