强制PostgreSQL在查询中使用哈希联接
我正在做一些性能调整,postgres日志中出现了一个特定的查询 粘贴在下面的查询:强制PostgreSQL在查询中使用哈希联接,postgresql,performance,Postgresql,Performance,我正在做一些性能调整,postgres日志中出现了一个特定的查询 粘贴在下面的查询: select distinct sc.CAPABILITY_NAME as "CAPABILITY" from SYSTEM_CAPABILITY sc join ROLE_CAPABILITY urc on urc.CAPABILITY=sc.CAPABILITY_NAME join ROLE_CAPABILITY grc on grc.CAPABILITY=urc.CAPABILITY
select distinct sc.CAPABILITY_NAME as "CAPABILITY" from SYSTEM_CAPABILITY sc join ROLE_CAPABILITY urc on urc.CAPABILITY=sc.CAPABILITY_NAME join ROLE_CAPABILITY grc on grc.CAPABILITY=urc.CAPABILITY join GROUP_USER gu on gu.ROLE=urc.ROLE_NAME join GROUP_ROLE gr on gr.ROLE_NAME=grc.ROLE_NAME where gu.USERNAME='TEST123' and (gu.EXPIRY_DATE is null or gu.EXPIRY_DATE>'2020-11-17 00:00:00+01') and gr.ID=gu.GROUP_ID and gu.GROUP_ID='ABC123'
当我直接对数据库运行这个查询时,需要几毫秒,但是当它从Java应用程序运行时,需要2秒钟以上
我在postgres配置文件中启用了以下选项。
会话\u预加载\u库='auto\u explain'
auto_explain.log_min_duration='1s'
从my local运行查询时,我可以看到,与Java应用程序的运行相比,my local使用哈希连接,而Java应用程序的运行使用嵌套循环
然后,我启用了设置enable_hashjoin=on作为测试,但看起来通过Java应用程序的测试仍然使用嵌套循环。我在运行Java应用程序的同时运行了这个查询,但只需要17ms,并且使用了hash_连接
首先,对postgres配置文件进行这样的更改是明智的。我理解,这也可能影响其他查询,目前更像是评估影响的测试阶段
其次,除了设置enable_hashjoin=on之外,我还需要做些什么来防止嵌套循环成为查询的第一个调用点
自动解释的查询结果如下:
Java应用程序
< 2020-11-18 10:55:57.012 CET >LOG: duration: 2212.992 ms execute S_107: select distinct sc.CAPABILITY_NAME as "CAPABILITY" from SYSTEM_CAPABILITY sc join ROLE_CAPABILITY urc on urc.CAPABILITY=sc.CAPABILITY_NAME join ROLE_CAPABILITY grc on grc.CAPABILITY=urc.CAPABILITY join GROUP_USER gu on gu.ROLE=urc.ROLE_NAME join GROUP_ROLE gr on gr.ROLE_NAME=grc.ROLE_NAME where gu.USERNAME=$1 and (gu.EXPIRY_DATE is null or gu.EXPIRY_DATE>$2) and gr.ID=gu.GROUP_ID and gu.GROUP_ID=$3
< 2020-11-18 10:55:57.012 CET >DETAIL: parameters: $1 = 'TEST123', $2 = '2020-11-17 00:00:00+01', $3 = 'ABC123'
< 2020-11-18 10:55:57.014 CET >LOG: duration: 2212.984 ms plan:
Query Text: select distinct sc.CAPABILITY_NAME as "CAPABILITY" from SYSTEM_CAPABILITY sc join ROLE_CAPABILITY urc on urc.CAPABILITY=sc.CAPABILITY_NAME join ROLE_CAPABILITY grc on grc.CAPABILITY=urc.CAPABILITY join GROUP_USER gu on gu.ROLE=urc.ROLE_NAME join GROUP_ROLE gr on gr.ROLE_NAME=grc.ROLE_NAME where gu.USERNAME=$1 and (gu.EXPIRY_DATE is null or gu.EXPIRY_DATE>$2) and gr.ID=gu.GROUP_ID and gu.GROUP_ID=$3
HashAggregate (cost=78.40..78.44 rows=4 width=19)
Group Key: sc.capability_name
-> Nested Loop (cost=5.49..78.39 rows=4 width=19)
-> Nested Loop (cost=5.22..77.12 rows=4 width=38)
Join Filter: ((gu.role)::text = (urc.role_name)::text)
-> Nested Loop (cost=4.93..62.56 rows=30 width=36)
-> Nested Loop (cost=0.42..9.82 rows=1 width=85)
-> Index Only Scan using idx_gp_usr_gp_usrnm_rl_exp_dt on group_user gu (cost=0.42..8.46 rows=1 width=26)
Index Cond: ((group_id = ($3)::text) AND (username = ($1)::text))
Filter: ((expiry_date IS NULL) OR (expiry_date > $2))
-> Seq Scan on group_role gr (cost=0.00..1.35 rows=1 width=112)
Filter: ((id)::text = ($3)::text)
-> Bitmap Heap Scan on role_capability grc (cost=4.51..52.44 rows=30 width=33)
Recheck Cond: ((role_name)::text = (gr.role_name)::text)
-> Bitmap Index Scan on pk_role_capability (cost=0.00..4.51 rows=30 width=0)
Index Cond: ((role_name)::text = (gr.role_name)::text)
-> Index Scan using idx_role_capability_capability on role_capability urc (cost=0.28..0.42 rows=5 width=33)
Index Cond: ((capability)::text = (grc.capability)::text)
-> Index Only Scan using idx_system_capability_capability_name on system_capability sc (cost=0.28..0.31 rows=1 width=19)
Index Cond: (capability_name = (urc.capability)::text)
<2020-11-18 10:55:57.012 CET>日志:持续时间:2212.992毫秒执行S_107:选择不同的sc.CAPABILITY_名称作为“CAPABILITY”从系统能力sc加入角色能力urc上的urc.CAPABILITY=sc.CAPABILITY\u NAME加入角色能力grc上的grc.CAPABILITY=urc.CAPABILITY加入组用户gu上gu.ROLE=urc.ROLE\u NAME加入组角色gr上gr.ROLE\u NAME=grc.ROLE\u NAME,其中gu.USERNAME=$1和(gu.expiration\u日期为空或gu.expiration\u日期>$2)gr.ID=gu.GROUP\U ID和gu.GROUP\U ID=3美元
<2020-11-18 10:55:57.012 CET>详细信息:参数:$1='TEST123',$2='2020-11-17 00:00:00+01',$3='ABC123'
<2020-11-18 10:55:57.014 CET>日志:持续时间:2212.984毫秒计划:
查询文本:从系统中选择不同的sc.CAPABILITY\u名称作为“CAPABILITY”,sc-CAPABILITY-join-ROLE\u-NAME-join-ROLE\u-CAPABILITY-grc-on-grc.CAPABILITY=urc.CAPABILITY-join-GROUP\u-USER-gu-on-gu.ROLE=urc.ROLE\u-NAME-join-GROUP\u-ROLE-gr-on-ROLE\u-NAME其中gu.USERNAME=$1和(gu.expiration\u DATE为null或gu.expiration\u DATE>$2)且gr.ID=gu.GROUP\u ID和gu.GROUP\u ID=$3
HashAggregate(成本=78.40..78.44行=4宽度=19)
组密钥:sc.capability\u name
->嵌套循环(成本=5.49..78.39行=4宽度=19)
->嵌套循环(成本=5.22..77.12行=4宽度=38)
加入筛选器:((gu.role)::text=(urc.role\u名称)::text)
->嵌套循环(成本=4.93..62.56行=30宽度=36)
->嵌套循环(成本=0.42..9.82行=1宽度=85)
->使用组用户gu上的idx_gp_usr_gp_usrnm_rl_exp_dt仅索引扫描(成本=0.42..8.46行=1宽度=26)
索引条件:((组id=($3)::文本)和(用户名=($1::文本))
筛选器:((到期日为空)或(到期日>$2))
->组角色gr上的顺序扫描(成本=0.00..1.35行=1宽度=112)
筛选器:((id)::text=($3)::text)
->角色功能grc上的位图堆扫描(成本=4.51..52.44行=30宽度=33)
重新检查条件:((角色名称)::text=(组角色名称)::text)
->pk_角色_功能上的位图索引扫描(成本=0.00..4.51行=30宽度=0)
索引条件:((角色名称)::文本=(组角色名称)::文本)
->在角色能力urc上使用idx角色能力进行索引扫描(成本=0.28..0.42行=5宽度=33)
索引条件:((能力)::文本=(grc.能力)::文本)
->在system_capability sc上使用idx_system_capability_名称仅索引扫描(成本=0.28..0.31行=1宽度=19)
索引条件:(能力名称=(资源能力)::文本)
从连接到DB的本地计算机运行
< 2020-11-18 10:58:50.297 CET >LOG: duration: 16.070 ms plan:
Query Text:
select distinct sc.CAPABILITY_NAME as "CAPABILITY" from SYSTEM_CAPABILITY sc join ROLE_CAPABILITY urc on urc.CAPABILITY=sc.CAPABILITY_NAME join ROLE_CAPABILITY grc on grc.CAPABILITY=urc.CAPABILITY join GROUP_USER gu on gu.ROLE=urc.ROLE_NAME join GROUP_ROLE gr on gr.ROLE_NAME=grc.ROLE_NAME where gu.USERNAME='TEST123' and (gu.EXPIRY_DATE is null or gu.EXPIRY_DATE>'2020-11-17 00:00:00+01') and gr.ID=gu.GROUP_ID and gu.GROUP_ID='ABC123'
HashAggregate (cost=93.77..94.27 rows=50 width=19) (actual time=15.997..16.021 rows=200 loops=1)
Group Key: sc.capability_name
-> Hash Join (cost=16.86..93.65 rows=50 width=19) (actual time=0.325..15.295 rows=2329 loops=1)
Hash Cond: ((urc.role_name)::text = (gu.role)::text)
-> Nested Loop (cost=5.07..78.20 rows=169 width=77) (actual time=0.139..14.106 rows=5861 loops=1)
Join Filter: ((sc.capability_name)::text = (urc.capability)::text)
-> Nested Loop (cost=4.79..63.64 rows=30 width=82) (actual time=0.132..5.677 rows=1076 loops=1)
-> Nested Loop (cost=4.51..54.09 rows=30 width=63) (actual time=0.124..0.447 rows=1076 loops=1)
-> Seq Scan on group_role gr (cost=0.00..1.35 rows=1 width=112) (actual time=0.015..0.018 rows=1 loops=1)
Filter: ((id)::text = 'ABC123'::text)
Rows Removed by Filter: 27
-> Bitmap Heap Scan on role_capability grc (cost=4.51..52.44 rows=30 width=33) (actual time=0.107..0.302 rows=1076 loops=1)
Recheck Cond: ((role_name)::text = (gr.role_name)::text)
Heap Blocks: exact=37
-> Bitmap Index Scan on pk_role_capability (cost=0.00..4.51 rows=30 width=0) (actual time=0.098..0.098 rows=1076 loops=1)
Index Cond: ((role_name)::text = (gr.role_name)::text)
-> Index Only Scan using idx_system_capability_capability_name on system_capability sc (cost=0.28..0.31 rows=1 width=19) (actual time=0.004..0.005 rows=1 loops=1076)
Index Cond: (capability_name = (grc.capability)::text)
Heap Fetches: 1076
-> Index Scan using idx_role_capability_capability on role_capability urc (cost=0.28..0.42 rows=5 width=33) (actual time=0.005..0.007 rows=5 loops=1076)
Index Cond: ((capability)::text = (grc.capability)::text)
-> Hash (cost=11.62..11.62 rows=13 width=26) (actual time=0.179..0.179 rows=279 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 24kB
-> Index Only Scan using idx_gp_usr_gp_usrnm_rl_exp_dt on group_user gu (cost=0.42..11.62 rows=13 width=26) (actual time=0.023..0.110 rows=279 loops=1)
Index Cond: ((group_id = 'ABC123'::text) AND (username = 'TEST123'::text))
Filter: ((expiry_date IS NULL) OR (expiry_date > '2020-11-16 23:00:00+00'::timestamp with time zone))
Heap Fetches: 4
<2020-11-18 10:58:50.297 CET>日志:持续时间:16.070毫秒计划:
查询文本:
从系统中选择不同的sc.CAPABILITY\u NAME作为“CAPABILITY”,sc加入角色\u能力urc在urc上。CAPABILITY=sc.CAPABILITY\u NAME加入角色\u能力grc在grc上。CAPABILITY=urc.CAPABILITY加入组\u用户gu在gu上。ROLE=urc.ROLE\u NAME加入组\u角色gr在gr.ROLE\u NAME=grc.ROLE\u名称,其中gu.USERNAME='TEST123'和(gu.expiration\u DATE为空或gu.expiration\u DATE>'2020-11-17 00:00:00+01')且gr.ID=gu.GROUP\u ID和gu.GROUP\u ID='ABC123'
HashAggregate(成本=93.77..94.27行=50宽度=19)(实际时间=15.997..16.021行=200循环=1)
组密钥:sc.capability\u name
->散列联接(成本=16.86..93.65行=50宽度=19)(实际时间=0.325..15.295行=2329循环=1)
散列条件:((urc.role_名称)::text=(gu.role)::text)
->嵌套循环(成本=5.07..78.20行=169宽度=77)(实际时间=0.139..14.106行=5861循环=1)
连接筛选器:((sc.capability_name)::text=(urc.capability)::text)
->嵌套循环(成本=4.79..63.64行=30宽度=82)(实际时间=0.132..5.677行=1076个循环=1)
->嵌套循环(成本=4.51..54.09行=30宽度=63)(实际时间=0.124..0.447行=1076个循环=1)
->对组角色gr的顺序扫描(成本=0.00..1.35行=1宽度=112)(实际时间=0.015..0.018行=1圈=1)
筛选器:((id)::text='ABC123'::text)
被筛选器删除的行:27
->角色功能grc上的位图堆扫描(成本=4.51..52.44行=30宽度=33)(实际时间=0.107..0.302行=1076循环=1)
复查条件