现代CPU模型具有大量的核心。多年来,一直在向数据库发送查询并行应用程序。哪里有报告查询,处理许多表行,查询使用多个cpu的能力可以帮助我们更快的执行。并行查询在PostgreSQL允许我们利用多个cpu完成报表查询速度。9.6实现的并行查询功能和帮助。从PostgreSQL 9.6报表查询可以使用多个cpu,完成得更快。
的初始实现并行查询执行了三年。并行支持需要更改代码在很多查询执行阶段。PostgreSQL 9.6代码创建了一个基础设施进一步改善。后来的版本扩展并行执行支持其他类型的查询。
限制
- 不支持并行执行如果所有CPU核已经饱和。从其他查询并行执行偷了CPU时间,增加响应时间。
- 最重要的是,并行处理显著增加内存使用量WORK_MEM高值,因为每个散列连接或排序操作WORK_MEM数量的内存。
- 接下来,低延迟OLTP查询不能做出任何与并行执行速度。特别是,查询返回一行可以执行严重当启用并行执行。
- 开发人员的泉水是tpc - h基准测试。检查如果你有类似的查询最好的并行执行。
- 并行执行只支持SELECT查询谓词没有锁。
- 适当的索引可能是一个更好的选择一个平行顺序表扫描。
- 不支持游标或暂停查询。
- 窗口的功能和有序集聚合函数非并行。
- 没有好处的IO-bound工作量。
- 没有并行排序算法。然而,查询类型仍然可以在某些方面是平行的。
- 取代CTE(…)与子选择支持并行执行。
- 目前国外数据包装不支持并行执行(但他们可能!)
- 不支持全外连接。
- 客户端设置max_rows禁用并行执行。
- 如果一个查询使用一个函数,它没有标记为平行的安全,这将是单线程的。
- SERIALIZABLE事务隔离级别禁用并行执行。
测试环境
PostgreSQL开发团队试图改善tpc - h基准测试查询的响应时间。你可以下载和基准<一个class="external-link" href="https://github.com/tvondra/pg_tpch" rel="nofollow">它适应PostgreSQL通过使用这些指令一个>。这不是一个官方的方式使用tpc - h基准测试,所以你不应该使用它来比较不同的数据库或硬件。
- 下载TPC-H_Tools_v2.17.3。邮政编码(或更新版本)<一个href="http://www.tpc.org/tpc_documents_current_versions/current_specifications.asp">从TPC官方网站一个>。
- 重命名makefile。套件Makefile和按要求修改它<一个href="https://github.com/tvondra/pg_tpch">https://github.com/tvondra/pg_tpch一个>。编译代码和命令
- 生成数据:。/ dbgen - s 10生成23 gb的数据库就足以看到不同的并行和非并行查询的性能。
- 资源描述文件转换为csv为+ sed
- 克隆pg_tpch库和csv文件复制到pg_tpch / dss /数据
- 生成与qgen查询命令
- 加载数据到数据库。/ tpch。sh命令。
平行顺序扫描
这可能不是因为并行读取更快,但由于散射数据跨多个CPU核心。现代操作系统提供了良好的PostgreSQL数据的缓存文件。预读允许得到一块从存储的不仅仅是要求PG守护进程。因此,查询性能并不局限由于磁盘IO。它消耗CPU周期:
- 从表数据页面读取行一个接一个
- 比较行值和条件
让我们试着执行简单的select查询:
|
1
2
3
4
5
6
7
8
|
tpch
=
#解释分析选择l_quantity sum_qty从lineitem l_shipdate & lt; =日期“1998-12-01”——间隔的105天;
查询
计划
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
Seq
扫描
在
lineitem
(
成本
=
0.00,1964772.00
行
=
58856235
宽度
=
5
)
(
实际
时间
=
0.014,16951.669
行
=
58839715
循环
=
1
)
过滤器
:
(
l_shipdate
&
lt
;
=
“1998-08-18”就是
::
时间戳
没有
时间
区
)
行
删除
通过
过滤器
:
1146337
规划
时间
:
0.203
女士
执行
时间
:
19035.100
女士
|
顺序扫描产生太多行不聚合。因此,执行查询由单个CPU核心。
添加SUM()之后,很明显看到,两名工人将帮助我们更快地使查询:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
解释
分析
选择
总和
(
l_quantity
)
作为
sum_qty
从
lineitem
在哪里
l_shipdate
&
lt
;
=
日期
“1998-12-01”
- - - - - -
时间间隔
“105”
一天
;
查询
计划
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
完成
总
(
成本
=
1589702.14,1589702.15
行
=
1
宽度
=
32
)
(
实际
时间
=
8553.365,8553.365
行
=
1
循环
=
1
)
- - - - - -
&
gt
;
收集
(
成本
=
1589701.91,1589702.12
行
=
2
宽度
=
32
)
(
实际
时间
=
8553.241,8555.067
行
=
3
循环
=
1
)
工人
计划
:
2
工人
推出了
:
2
- - - - - -
&
gt
;
部分
总
(
成本
=
1588701.91,1588701.92
行
=
1
宽度
=
32
)
(
实际
时间
=
8547.546,8547.546
行
=
1
循环
=
3
)
- - - - - -
&
gt
;
平行
Seq
扫描
在
lineitem
(
成本
=
0.00,1527393.33
行
=
24523431
宽度
=
5
)
(
实际
时间
=
0.038,5998.417
行
=
19613238
循环
=
3
)
过滤器
:
(
l_shipdate
&
lt
;
=
“1998-08-18”就是
::
时间戳
没有
时间
区
)
行
删除
通过
过滤器
:
382112年
规划
时间
:
0.241
女士
执行
时间
:
8555.131
女士
|
更复杂的查询是快2.2倍比平原,单线程的选择。
并行聚合
“平行Seq扫描”节点产生行部分聚合。“部分聚合”节点可以减少这些行SUM ()。最后,从每个工人和计数器收集“收集”节点。
最终结果由“确定骨料”计算节点。如果你有你自己的聚合函数,不要忘记他们标记为“平行安全”。
工人数量
我们可以增加员工的数量没有服务器重启:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
改变
系统
集
max_parallel_workers_per_gather
=
4
;
select *
从
pg_reload_conf
(
)
;
现在
,
在那里
是
4
工人
在
解释
输出
:
tpch
=
#解释分析选择总和(l_quantity)从lineitem sum_qty l_shipdate & lt; =日期“1998-12-01”——间隔的105天;
查询
计划
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
完成
总
(
成本
=
1440213.58,1440213.59
行
=
1
宽度
=
32
)
(
实际
时间
=
5152.072,5152.072
行
=
1
循环
=
1
)
- - - - - -
&
gt
;
收集
(
成本
=
1440213.15,1440213.56
行
=
4
宽度
=
32
)
(
实际
时间
=
5151.807,5153.900
行
=
5
循环
=
1
)
工人
计划
:
4
工人
推出了
:
4
- - - - - -
&
gt
;
部分
总
(
成本
=
1439213.15,1439213.16
行
=
1
宽度
=
32
)
(
实际
时间
=
5147.238,5147.239
行
=
1
循环
=
5
)
- - - - - -
&
gt
;
平行
Seq
扫描
在
lineitem
(
成本
=
0.00,1402428.00
行
=
14714059
宽度
=
5
)
(
实际
时间
=
0.037,3601.882
行
=
11767943
循环
=
5
)
过滤器
:
(
l_shipdate
&
lt
;
=
“1998-08-18”就是
::
时间戳
没有
时间
区
)
行
删除
通过
过滤器
:
229267年
规划
时间
:
0.218
女士
执行
时间
:
5153.967
女士
|
这里发生了什么?我们已经改变了工人的数量从2到4,但查询只有1.6599倍。实际上,比例是惊人的。我们有两个工人加上一个领导人。配置改变后,它变成了4 + 1。
最大的改进的并行执行,我们可以实现是:5/3 = 1.66 (6)X更快。
它是如何工作的呢?
流程
查询执行总是开始在“领袖”的过程。一个领导者执行所有非并行处理活动和自己的贡献。其他进程执行相同的查询被称为“工人”的过程。并行执行利用<一个class="external-link" href="https://www.postgresql.org/docs/11/bgworker.html" rel="nofollow">动态背景的工人一个>9。4基础设施(添加)。PostgreSQL的其他部分使用流程,但不支持线程,查询创建三个工作进程可能比传统快4倍执行。
沟通
使用消息队列工人与领导沟通(基于共享内存)。每个进程都有两个队列:一个错误和第二个元组。
许多工人如何使用?
首先,<一个class="external-link" href="https://www.postgresql.org/docs/11/runtime-config-resource.html" rel="nofollow">max_parallel_workers_per_gather一个>参数是最小的限制工人的数量。其次,查询执行器需要工人从池中受到限制<一个class="external-link" href="https://www.postgresql.org/docs/11/runtime-config-resource.html" rel="nofollow">max_parallel_workers大小一个>。最后,顶级极限<一个class="external-link" href="https://www.postgresql.org/docs/11/runtime-config-resource.html" rel="nofollow">max_worker_processes一个>:后台进程的总数。
没有工人分配会导致单个进程的执行。
查询计划可以考虑减少工人数量基于一个表或索引的大小。<一个class="external-link" href="https://www.postgresql.org/docs/current/runtime-config-query.html" rel="nofollow">min_parallel_table_scan_size一个>和<一个class="external-link" href="https://www.postgresql.org/docs/current/runtime-config-query.html" rel="nofollow">min_parallel_index_scan_size一个>控制这种行为。
|
1
2
3
4
5
|
集
min_parallel_table_scan_size
=
8 mb的
8 mb
表
= &
gt
;
1
工人
24 mb
表
= &
gt
;
2
工人
72 mb
表
= &
gt
;
3
工人
x
= &
gt
;
日志
(
x
/
min_parallel_table_scan_size
)
/
日志
(
3
)
+
1
工人
|
每次表3 x大于min_parallel_(索引|表)_scan_size postgres添加一个工人。工人的数量不是基于成本!一个圆形依赖复杂的实现困难。相反,计划使用简单的规则。
在实践中,这些规则并不总是在生产中可接受的工人数量,你可以覆盖特定表的ALTER table…(parallel_workers = N)。
为什么不使用并行执行?
除了长串并行执行的限制,PostgreSQL检查成本:
parallel_setup_cost一个>为了避免简称并行执行查询。这对内存设置模型所花费的时间,过程开始,最初的沟通
parallel_tuple_cost一个>:领导者和员工之间的沟通可能需要很长时间。时间发送的元组工人的数量成正比。参数模型的通信成本。
嵌套循环联接
PostgreSQL 9.6 +可以并行执行“嵌套循环”由于操作的简单性。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
解释
(
成本
从
)
选择
c_custkey
,
数
(
o_orderkey
)
从
客户
左
外
加入
订单
在
c_custkey
=
o_custkey
和
o_comment
不
就像
“% %特殊%存款”
集团
通过
c_custkey
;
查询
计划
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
完成
GroupAggregate
集团
关键
:
客户
。
c_custkey
- - - - - -
&
gt
;
收集
合并
工人
计划
:
4
- - - - - -
&
gt
;
部分
GroupAggregate
集团
关键
:
客户
。
c_custkey
- - - - - -
&
gt
;
嵌套的
循环
左
加入
- - - - - -
&
gt
;
平行
指数
只有
扫描
使用
customer_pkey
在
客户
- - - - - -
&
gt
;
指数
扫描
使用
idx_orders_custkey
在
订单
指数
气孔导度
:
(
客户
。
c_custkey
=
o_custkey
)
过滤器
:
(
(
o_comment
)
::
文本
!
~
~
“% %特殊%存款”
::
文本
)
|
收集发生在最后一个阶段,所以“嵌套循环连接”是并行操作。“平行索引只扫描”可以从版本10。它的作用类似于一个并行的顺序扫描。的c_custkey=o_custkey读取单个条件根据每个客户的订单行。因此它不是平行的。
散列连接
每个工人都建立自己的哈希表,直到PostgreSQL 11。因此,4 +工人无法提高性能。新的实现使用一个共享的哈希表。每个工人可以利用WORK_MEM建立哈希表。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20.
21
22
23
24
25
26
27
28
29日
30.
31日
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
选择
l_shipmode
,
总和
(
情况下
当
o_orderpriority
=
“1-URGENT”
或
o_orderpriority
=
“第二中学”
然后
1
其他的
0
结束
)
作为
high_line_count
,
总和
(
情况下
当
o_orderpriority
&
lt
;
&
gt
;
“1-URGENT”
和
o_orderpriority
&
lt
;
&
gt
;
“第二中学”
然后
1
其他的
0
结束
)
作为
low_line_count
从
订单
,
lineitem
在哪里
o_orderkey
=
l_orderkey
和
l_shipmode
在
(
“邮件”
,
“空气”
)
和
l_commitdate
&
lt
;
l_receiptdate
和
l_shipdate
&
lt
;
l_commitdate
和
l_receiptdate
&
gt
;
=
日期
“1996-01-01”
和
l_receiptdate
&
lt
;
日期
“1996-01-01”
+
时间间隔
' 1 '
一年
集团
通过
l_shipmode
订单
通过
l_shipmode
限制
1
;
查询
计划
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - - - - -
限制
(
成本
=
1964755.66,1964961.44
行
=
1
宽度
=
27
)
(
实际
时间
=
7579.592,7922.997
行
=
1
循环
=
1
)
- - - - - -
&
gt
;
完成
GroupAggregate
(
成本
=
1964755.66,1966196.11
行
=
7
宽度
=
27
)
(
实际
时间
=
7579.590,7579.591
行
=
1
循环
=
1
)
集团
关键
:
lineitem
。
l_shipmode
- - - - - -
&
gt
;
收集
合并
(
成本
=
1964755.66,1966195.83
行
=
28
宽度
=
27
)
(
实际
时间
=
7559.593,7922.319
行
=
6
循环
=
1
)
工人
计划
:
4
工人
推出了
:
4
- - - - - -
&
gt
;
部分
GroupAggregate
(
成本
=
1963755.61,1965192.44
行
=
7
宽度
=
27
)
(
实际
时间
=
7548.103,7564.592
行
=
2
循环
=
5
)
集团
关键
:
lineitem
。
l_shipmode
- - - - - -
&
gt
;
排序
(
成本
=
1963755.61,1963935.20
行
=
71838年
宽度
=
27
)
(
实际
时间
=
7530.280,7539.688
行
=
62519年
循环
=
5
)
排序
关键
:
lineitem
。
l_shipmode
排序
方法
:
外部
合并
磁盘
:
2304 kb
工人
0
:
排序
方法
:
外部
合并
磁盘
:
2064 kb
工人
1
:
排序
方法
:
外部
合并
磁盘
:
2384 kb
工人
2
:
排序
方法
:
外部
合并
磁盘
:
2264 kb
工人
3
:
排序
方法
:
外部
合并
磁盘
:
2336 kb
- - - - - -
&
gt
;
平行
哈希
加入
(
成本
=
382571.01,1957960.99
行
=
71838年
宽度
=
27
)
(
实际
时间
=
7036.917,7499.692
行
=
62519年
循环
=
5
)
哈希
气孔导度
:
(
lineitem
。
l_orderkey
=
订单
。
o_orderkey
)
- - - - - -
&
gt
;
平行
Seq
扫描
在
lineitem
(
成本
=
0.00,1552386.40
行
=
71838年
宽度
=
19
)
(
实际
时间
=
0.583,4901.063
行
=
62519年
循环
=
5
)
过滤器
:
(
(
l_shipmode
=
任何
(
}{邮件、空气的
::
bpchar
(
]
)
)
和
(
l_commitdate
&
lt
;
l_receiptdate
)
和
(
l_shipdate
&
lt
;
l_commitdate
)
和
(
l_receiptdate
&
gt
;
=
“1996-01-01”
::
日期
)
和
(
l_receiptdate
&
lt
;
“1997-01-01”就是
::
时间戳
没有
时间
区
)
)
行
删除
通过
过滤器
:
11934691
- - - - - -
&
gt
;
平行
哈希
(
成本
=
313722.45,313722.45
行
=
3750045
宽度
=
20.
)
(
实际
时间
=
2011.518,2011.518
行
=
3000000
循环
=
5
)
桶
:
65536年
批次
:
256年
内存
使用
:
3840 kb
- - - - - -
&
gt
;
平行
Seq
扫描
在
订单
(
成本
=
0.00,313722.45
行
=
3750045
宽度
=
20.
)
(
实际
时间
=
0.029,995.948
行
=
3000000
循环
=
5
)
规划
时间
:
0.977
女士
执行
时间
:
7923.770
女士
|
tpc - h是一个很好的实例的查询12一个并行的散列连接。每个工人可以帮助建立一个共享的哈希表。
合并连接
由于合并连接的本质是不可能让它平行。不要担心如果是查询执行的最后阶段仍然可以可以看到并行执行查询合并联接。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20.
21
22
23
24
25
26
27
28
29日
30.
31日
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61年
62年
63年
64年
65年
|
- - -
查询
2
从
TPC
- - - - - -
H
解释
(
成本
从
)
选择
s_acctbal
,
s_name
,
n_name
,
p_partkey
,
p_mfgr
,
s_address
,
s_phone
,
s_comment
从
部分
,
供应商
,
partsupp
,
国家
,
地区
在哪里
p_partkey
=
ps_partkey
和
s_suppkey
=
ps_suppkey
和
p_size
=
36
和
p_type
就像
%黄铜的
和
s_nationkey
=
n_nationkey
和
n_regionkey
=
r_regionkey
和
r_name
=
“美国”
和
ps_supplycost
=
(
选择
最小值
(
ps_supplycost
)
从
partsupp
,
供应商
,
国家
,
地区
在哪里
p_partkey
=
ps_partkey
和
s_suppkey
=
ps_suppkey
和
s_nationkey
=
n_nationkey
和
n_regionkey
=
r_regionkey
和
r_name
=
“美国”
)
订单
通过
s_acctbal
desc
,
n_name
,
s_name
,
p_partkey
限制
One hundred.
;
查询
计划
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
限制
- - - - - -
&
gt
;
排序
排序
关键
:
供应商
。
s_acctbal
DESC
,
国家
。
n_name
,
供应商
。
s_name
,
部分
。
p_partkey
- - - - - -
&
gt
;
合并
加入
合并
气孔导度
:
(
部分
。
p_partkey
=
partsupp
。
ps_partkey
)
加入
过滤器
:
(
partsupp
。
ps_supplycost
=
(
辅助方案
1
)
)
- - - - - -
&
gt
;
收集
合并
工人
计划
:
4
- - - - - -
&
gt
;
平行
指数
扫描
使用
<
强大的
>
part_pkey
<
/
强大的
>
在
部分
过滤器
:
(
(
(
p_type
)
::
文本
~
~
%黄铜的
::
文本
)
和
(
p_size
=
36
)
)
- - - - - -
&
gt
;
实现
- - - - - -
&
gt
;
排序
排序
关键
:
partsupp
。
ps_partkey
- - - - - -
&
gt
;
嵌套的
循环
- - - - - -
&
gt
;
嵌套的
循环
加入
过滤器
:
(
国家
。
n_regionkey
=
地区
。
r_regionkey
)
- - - - - -
&
gt
;
Seq
扫描
在
地区
过滤器
:
(
r_name
=
“美国”
::
bpchar
)
- - - - - -
&
gt
;
哈希
加入
哈希
气孔导度
:
(
供应商
。
s_nationkey
=
国家
。
n_nationkey
)
- - - - - -
&
gt
;
Seq
扫描
在
供应商
- - - - - -
&
gt
;
哈希
- - - - - -
&
gt
;
Seq
扫描
在
国家
- - - - - -
&
gt
;
指数
扫描
使用
idx_partsupp_suppkey
在
partsupp
指数
气孔导度
:
(
ps_suppkey
=
供应商
。
s_suppkey
)
辅助方案
1
- - - - - -
&
gt
;
总
- - - - - -
&
gt
;
嵌套的
循环
加入
过滤器
:
(
nation_1
。
n_regionkey
=
region_1
。
r_regionkey
)
- - - - - -
&
gt
;
Seq
扫描
在
地区
region_1
过滤器
:
(
r_name
=
“美国”
::
bpchar
)
- - - - - -
&
gt
;
嵌套的
循环
- - - - - -
&
gt
;
嵌套的
循环
- - - - - -
&
gt
;
指数
扫描
使用
idx_partsupp_partkey
在
partsupp
partsupp_1
指数
气孔导度
:
(
部分
。
p_partkey
=
ps_partkey
)
- - - - - -
&
gt
;
指数
扫描
使用
supplier_pkey
在
供应商
supplier_1
指数
气孔导度
:
(
s_suppkey
=
partsupp_1
。
ps_suppkey
)
- - - - - -
&
gt
;
指数
扫描
使用
nation_pkey
在
国家
nation_1
指数
气孔导度
:
(
n_nationkey
=
supplier_1
。
s_nationkey
)
|
上面的“合并连接”节点是“收集合并”。因此没有使用并行执行合并。但“并行索引扫描”节点仍有助于part_pkey段。
Partition-wise加入
PostgreSQL 11禁用<一个class="external-link" href="http://ashutoshpg.blogspot.com/2017/12/partition-wise-joins-divide-and-conquer.html" rel="nofollow">partition-wise加入一个>默认功能。Partition-wise加入计划成本高。同样能做partition-by-partition分区表的连接。这允许postgres使用更小的哈希表。每个每个分区连接操作可以并行执行。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20.
21
22
23
24
25
26
27
28
29日
30.
31日
32
33
34
35
36
37
38
39
40
41
42
43
|
tpch
=
#设置enable_partitionwise_join = t;
tpch
=
#解释(成本)select * from prt1 t1, prt2 t2
在哪里
t1
。
一个
=
t2
。
b
和
t1
。
b
=
0
和
t2
。
b
之间的
0
和
10000年
;
查询
计划
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - - - - -
附加
- - - - - -
&
gt
;
哈希
加入
哈希
气孔导度
:
(
t2
。
b
=
t1
。
一个
)
- - - - - -
&
gt
;
Seq
扫描
在
prt2_p1
t2
过滤器
:
(
(
b
&
gt
;
=
0
)
和
(
b
&
lt
;
=
10000年
)
)
- - - - - -
&
gt
;
哈希
- - - - - -
&
gt
;
Seq
扫描
在
prt1_p1
t1
过滤器
:
(
b
=
0
)
- - - - - -
&
gt
;
哈希
加入
哈希
气孔导度
:
(
t2_1
。
b
=
t1_1
。
一个
)
- - - - - -
&
gt
;
Seq
扫描
在
prt2_p2
t2_1
过滤器
:
(
(
b
&
gt
;
=
0
)
和
(
b
&
lt
;
=
10000年
)
)
- - - - - -
&
gt
;
哈希
- - - - - -
&
gt
;
Seq
扫描
在
prt1_p2
t1_1
过滤器
:
(
b
=
0
)
tpch
=
#设置parallel_setup_cost = 1;
tpch
=
#设置parallel_tuple_cost = 0.01;
tpch
=
#解释(成本)select * from prt1 t1, prt2 t2
在哪里
t1
。
一个
=
t2
。
b
和
t1
。
b
=
0
和
t2
。
b
之间的
0
和
10000年
;
查询
计划
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - - - - -
收集
工人
计划
:
4
- - - - - -
&
gt
;
平行
附加
- - - - - -
&
gt
;
平行
哈希
加入
哈希
气孔导度
:
(
t2_1
。
b
=
t1_1
。
一个
)
- - - - - -
&
gt
;
平行
Seq
扫描
在
prt2_p2
t2_1
过滤器
:
(
(
b
&
gt
;
=
0
)
和
(
b
&
lt
;
=
10000年
)
)
- - - - - -
&
gt
;
平行
哈希
- - - - - -
&
gt
;
平行
Seq
扫描
在
prt1_p2
t1_1
过滤器
:
(
b
=
0
)
- - - - - -
&
gt
;
平行
哈希
加入
哈希
气孔导度
:
(
t2
。
b
=
t1
。
一个
)
- - - - - -
&
gt
;
平行
Seq
扫描
在
prt2_p1
t2
过滤器
:
(
(
b
&
gt
;
=
0
)
和
(
b
&
lt
;
=
10000年
)
)
- - - - - -
&
gt
;
平行
哈希
- - - - - -
&
gt
;
平行
Seq
扫描
在
prt1_p1
t1
过滤器
:
(
b
=
0
)
|
最重要的是,partition-wise连接可以使用并行执行只有在足够大的分区。
平行的附加
平行的附加一个>分区的工作,而不是在不同的员工使用不同的块。通常,你可以看到这个联盟所有查询。缺点——更少的并行性,因为每个工人可能最终为一个查询工作。
只有两个工人发起了即使启用了4名工人。
|
1
2
3
4
5
6
7
8
9
10
11
12
|
tpch
=
#(成本)选择和解释(l_quantity)从lineitem sum_qty l_shipdate & lt; =日期“1998-12-01”——间隔的105天联盟所有选择(l_quantity)之和作为sum_qty lineitem l_shipdate & lt; =日期“2000-12-01”——间隔的105天;
查询
计划
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
收集
工人
计划
:
2
- - - - - -
&
gt
;
平行
附加
- - - - - -
&
gt
;
总
- - - - - -
&
gt
;
Seq
扫描
在
lineitem
过滤器
:
(
l_shipdate
&
lt
;
=
“2000-08-18”就是
::
时间戳
没有
时间
区
)
- - - - - -
&
gt
;
总
- - - - - -
&
gt
;
Seq
扫描
在
lineitem
lineitem_1
过滤器
:
(
l_shipdate
&
lt
;
=
“1998-08-18”就是
::
时间戳
没有
时间
区
)
|
最重要的变量
- WORK_MEM限制每个进程的内存使用情况!不仅对查询:work_mem *过程*连接= >可能导致重大的内存使用。
- max_parallel_workers_per_gather一个>- - - - - -- - - - - -一个执行人将使用多少工人计划节点的并行执行
- max_worker_processes一个>- - - - - -- - - - - -适应工人总数的CPU核的数量在一个服务器上安装
- max_parallel_workers一个>- - - - - -- - - - - -相同的平行的工人的数量
总结
从9.6开始并行查询执行复杂查询扫描行数可以显著改善性能或索引记录。在PostgreSQL 10中,并行执行是默认启用。不要忘记禁用并行执行在OLTP工作负载很重的服务器。顺序扫描或索引扫描仍然消耗大量的资源。如果你不运行报告对整个数据集,您可能提高查询性能,添加缺失索引或使用适当的分区。
引用
- https://www.postgresql.org/docs/11/how-parallel-query-works.html一个>
- https://www.postgresql.org/docs/11/parallel-plans.html一个>
- http://ashutoshpg.blogspot.com/2017/12/partition-wise-joins-divide-and-conquer.html一个>
- http://rhaas.blogspot.com/2016/04/postgresql - 96 - - -查询- vs.html平行一个><一个class="external-link" href="http://ashutoshpg.blogspot.com/2017/12/partition-wise-joins-divide-and-conquer.html" rel="nofollow">
- http://amitkapila16.blogspot.com/2015/11/parallel-sequential-scans-in-play.html一个>
- https://write-skew.blogspot.com/2018/01/parallel-hash-for-postgresql.html一个>
- http://rhaas.blogspot.com/2017/03/parallel-query-v2.html一个>
- https://blog.2ndquadrant.com/parallel-monster-benchmark/一个>
- https://blog.2ndquadrant.com/parallel-aggregate/一个>
- https://www.depesz.com/2018/02/12/waiting-for-postgresql-11-support-parallel-btree-index-builds/一个>
- 并行性在PostgreSQL 11一个>
- - - - - -
编译的照片形象<一个href="https://unsplash.com/photos/BYOzzo1xMU4?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">内森Gonthier一个>和<一个href="https://unsplash.com/photos/A-GZnF8EIGY?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">帕维尔Nekoranec一个>在<一个href="https://unsplash.com/search/photos/elephant?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash一个>





