GaussDB(DWS)性能调优:row_number() over()、count() over()改写解析
【摘要】 在SQL中,我们常常会用到row_number() over、count() over()这类窗口函数来实现在同一SQL中查询明细和汇总数据,而这有时会引入性能问题。本篇的主旨在于识别和解决这两个窗口函数所引起的性能瓶颈。实际上,窗口函数造成的性能瓶颈同SQL的易读性比起来,有时是可接受的,需要开发人员按照实际情况决定是否调优。本案例性能:调优前7s+ -> 调优后600-ms。
1、【问题描述】
row_number() over(), count() over()慢,执行计划中出现sort、WindowAgg,窗口函数集中在一个DN上运行。
2、【原始语句】
select
T2.*,
10 * (1 -1) + 1 as "startIndex",
/*开始的索引是第几条数据*/
10 * 1 as "endIndex",
/*结束的索引是第几条数据*/
1 as "curPage",
/*当前页数*/
10 AS "pageSize"
/*每页最大行数*/
from
(
SELECT
t.*,
row_number() over(
order by
t.risk_id
) as rn,
COUNT(t.risk_id) over() as totalRows
FROM
dmsalesw.dm_sale_risk_info_f t
where
1 = 1
and t.del_flag = 'N'
and t.object_type IN ('Proposal', 'Contract', 'PO', 'Change', 'Pre-PO')
AND t.creation_date >= to_date('2018-01-01', 'yyyy-mm-dd')
AND t.creation_date <= to_date('2023-10-08', 'yyyy-mm-dd')
AND t.REGION_CODE IN ('026699')
AND t.repoffice_code IN (
'046593',
'026876',
'045180',
'026467',
'047197',
'026699',
'026719',
'026850',
'026783',
'026734',
'026393'
)
) t2
where
t2.rn > 10 * (1 -1)
and t2.rn <= 10 * 1
*本篇对SQL进行了简化,实际开发不建议使用t.*这样的写法,因为会有不易读、维护困难的风险。
3、性能分析
执行计划中出现Sort和WindowAgg,第3~6步集中在一个DN上进行,使SQL非常缓慢。
id | operation | A-time | A-rows | E-rows | E-distinct | Peak Memory | E-memory | A-width | E-width | E-costs
----+------------------------------------------------------------------------+----------------------+--------+--------+------------+----------------+---------------+-------------+---------+-----------
1 | -> Row Adapter | 7552.782 | 10 | 271 | | 586KB | | | 2323 | 325788.75
2 | -> Vector Streaming (type: GATHER) | 7552.769 | 10 | 271 | | 1658KB | | | 2323 | 325788.75
3 | -> Vector Subquery Scan on t2 | [7088.125, 7088.125] | 10 | 271 | | [1MB, 1MB] | 1MB | | 2323 | 325624.85
4 | -> Vector WindowAgg | [7084.311, 7084.311] | 549662 | 54288 | | [35MB, 35MB] | 16MB | | 2278 | 324810.53
5 | -> Vector WindowAgg | [4233.740, 4233.740] | 549662 | 54288 | | [4MB, 4MB] | 16MB | | 2278 | 323860.49
6 | -> Vector Streaming(type: BROADCAST) | [4219.407, 4219.407] | 549662 | 54288 | | [142MB, 142MB] | 2MB | | 2278 | 323602.14
7 | -> Vector Sort | [313.823, 362.671] | 549662 | 54288 | | [108MB, 116MB] | 36MB(10234MB) | [1870,1885] | 2278 | 263217.34
8 | -> CStore Scan on dmsalesw.dm_sale_risk_info_f t | [162.208, 199.123] | 549662 | 54287 | | [14MB, 14MB] | 1MB | | 2278 | 263009.89
Datanode Information (identified by plan id)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 --Row Adapter
(actual time=7551.970..7552.782 rows=10 loops=1)
(CPU: ex c/r=3775, ex row=10, ex cyc=37756, inc cyc=22658165568)
2 --Vector Streaming (type: GATHER)
(actual time=7551.959..7552.769 rows=10 loops=1)
(Buffers: shared hit=1)
(CPU: ex c/r=2265812781, ex row=10, ex cyc=22658127812, inc cyc=22658127812)
3 --Vector Subquery Scan on t2
dn_6017_6018 (actual time=5824.504..7088.125 rows=10 loops=1) (filter time=3.363 projection time=0.014)
dn_6017_6018 (CPU: ex c/r=1158085, ex row=10, ex cyc=11580852, inc cyc=21264241428)
4 --Vector WindowAgg
dn_6017_6018 (actual time=5824.450..7084.311 rows=549662 loops=1) (projection time=27.965)
dn_6017_6018 (Buffers: temp read=95144 written=95144)
dn_6017_6018 (CPU: ex c/r=15558, ex row=549662, ex cyc=8551761036, inc cyc=21252660576)
5 --Vector WindowAgg
dn_6017_6018 (actual time=2.246..4233.740 rows=549662 loops=1) (projection time=2.198)
dn_6017_6018 (Buffers: 0)
dn_6017_6018 (CPU: ex c/r=78, ex row=549662, ex cyc=43182988, inc cyc=12700899540)
6 --Vector Streaming(type: BROADCAST)
dn_6017_6018 (actual time=1.792..4219.407 rows=549662 loops=1)
dn_6017_6018 (Buffers: 0)
dn_6017_6018 (CPU: ex c/r=23028, ex row=549662, ex cyc=12657716552, inc cyc=12657716552)
7 --Vector Sort
dn_6001_6002 (actual time=268.183..320.758 rows=34346 loops=1)
dn_6003_6004 (actual time=310.357..362.671 rows=34410 loops=1)
dn_6005_6006 (actual time=260.182..316.886 rows=34344 loops=1)
dn_6007_6008 (actual time=262.096..317.627 rows=34016 loops=1)
dn_6009_6010 (actual time=260.048..315.071 rows=34710 loops=1)
dn_6011_6012 (actual time=264.688..319.164 rows=34492 loops=1)
dn_6013_6014 (actual time=268.204..323.335 rows=34577 loops=1)
dn_6015_6016 (actual time=271.505..326.924 rows=34479 loops=1)
dn_6017_6018 (actual time=272.291..329.827 rows=34386 loops=1)
dn_6019_6020 (actual time=265.727..325.489 rows=34480 loops=1)
dn_6021_6022 (actual time=264.947..318.154 rows=34386 loops=1)
dn_6023_6024 (actual time=271.657..324.985 rows=34121 loops=1)
dn_6025_6026 (actual time=264.907..318.398 rows=34023 loops=1)
dn_6027_6028 (actual time=258.013..313.823 rows=33961 loops=1)
dn_6029_6030 (actual time=263.489..319.153 rows=34560 loops=1)
dn_6031_6032 (actual time=264.902..317.668 rows=34371 loops=1)
dn_6001_6002 (Buffers: shared hit=1109)
dn_6003_6004 (Buffers: shared hit=1109)
dn_6005_6006 (Buffers: shared hit=1109)
dn_6007_6008 (Buffers: shared hit=1109)
dn_6009_6010 (Buffers: shared hit=1109)
dn_6011_6012 (Buffers: shared hit=1109)
dn_6013_6014 (Buffers: shared hit=1109)
dn_6015_6016 (Buffers: shared hit=1109)
dn_6017_6018 (Buffers: shared hit=1109)
dn_6019_6020 (Buffers: shared hit=1109)
dn_6021_6022 (Buffers: shared hit=1109)
dn_6023_6024 (Buffers: shared hit=1109)
dn_6025_6026 (Buffers: shared hit=1109)
dn_6027_6028 (Buffers: shared hit=1109)
dn_6029_6030 (Buffers: shared hit=1109)
dn_6031_6032 (Buffers: shared hit=1109)
dn_6001_6002 (CPU: ex c/r=13275, ex row=34346, ex cyc=455973756, inc cyc=962230188)
dn_6003_6004 (CPU: ex c/r=14259, ex row=34410, ex cyc=490679080, inc cyc=1087977988)
dn_6005_6006 (CPU: ex c/r=13404, ex row=34344, ex cyc=460361872, inc cyc=950597836)
dn_6007_6008 (CPU: ex c/r=13453, ex row=34016, ex cyc=457632392, inc cyc=952845792)
dn_6009_6010 (CPU: ex c/r=13178, ex row=34710, ex cyc=457410672, inc cyc=945161948)
dn_6011_6012 (CPU: ex c/r=13345, ex row=34492, ex cyc=460305152, inc cyc=957453780)
dn_6013_6014 (CPU: ex c/r=13437, ex row=34577, ex cyc=464612820, inc cyc=969954784)
dn_6015_6016 (CPU: ex c/r=13759, ex row=34479, ex cyc=474397084, inc cyc=980714964)
dn_6017_6018 (CPU: ex c/r=13969, ex row=34386, ex cyc=480349072, inc cyc=989443104)
dn_6019_6020 (CPU: ex c/r=13795, ex row=34480, ex cyc=475660772, inc cyc=976420876)
dn_6021_6022 (CPU: ex c/r=13201, ex row=34386, ex cyc=453934712, inc cyc=954411488)
dn_6023_6024 (CPU: ex c/r=13580, ex row=34121, ex cyc=463384772, inc cyc=974913264)
dn_6025_6026 (CPU: ex c/r=13384, ex row=34023, ex cyc=455366400, inc cyc=955135348)
dn_6027_6028 (CPU: ex c/r=13392, ex row=33961, ex cyc=454837616, inc cyc=941417620)
dn_6029_6030 (CPU: ex c/r=13341, ex row=34560, ex cyc=461083360, inc cyc=957420708)
dn_6031_6032 (CPU: ex c/r=13182, ex row=34371, ex cyc=453109256, inc cyc=952914680)
为了消除对大量数据的WindowAgg,需要对SQL进行改写,目的是通过等价逻辑改写,消除窗口函数。
4、SQL改写
观察原SQL,我们发现对row_number() over()的使用在于对排序后,只取前N个;而对count() over()的使用在于每行显示risk_id的个数。我们可以进行如下改写:
explain performance
with t2 as (
SELECT
t.*
FROM
dmsalesw.dm_sale_risk_info_f t
where
1 = 1
and t.del_flag = 'N'
and t.object_type IN ('Proposal', 'Contract', 'PO', 'Change', 'Pre-PO')
AND t.creation_date >= to_date('2018-01-01', 'yyyy-mm-dd')
AND t.creation_date <= to_date('2023-10-08', 'yyyy-mm-dd')
AND t.REGION_CODE IN ('026699')
AND t.repoffice_code IN (
'046593',
'026876',
'045180',
'026467',
'047197',
'026699',
'026719',
'026850',
'026783',
'026734',
'026393'
)
)
select
t2.*,
"totalRows",
10 * (1 -1) + 1 as "startIndex",
/*开始的索引是第几条数据*/
10 * 1 as "endIndex",
/*结束的索引是第几条数据*/
1 as "curPage",
/*当前页数*/
10 AS "pageSize"
/*每页最大行数*/
from t2
left join (select count(t2.risk_id) as "totalRows" from t2)
on 1=1
order by risk_id
limit 10 * 1
offset 0;
改写后的SQL在逻辑上同原SQL是等价的,但是消除了WindowAgg造成的单DN上运行,语句可以在多个DN上执行。
改写逻辑:把t2写成with子查询以在join时使用其别名,使用left join (select count() from t2)代替count() over(),使用limit offset代替row_number() over()和对rn的过滤。
id | operation | A-time | A-rows | E-rows | E-distinct | Peak Memory | E-memory | A-width | E-width | E-costs
----+---------------------------------------------------------------------------------------+--------------------+--------+--------+------------+----------------+----------+-------------+---------+-----------
1 | -> Row Adapter | 518.182 | 10 | 10 | | 595KB | | | 2286 | 519287.14
2 | -> Vector Limit | 518.167 | 10 | 10 | | 24KB | | | 2286 | 519287.14
3 | -> Vector Streaming (type: GATHER) | 518.163 | 160 | 160 | | 29357KB | | | 2286 | 519372.36
4 | -> Vector Limit | [401.791, 503.709] | 160 | 160 | | [32KB, 32KB] | 1MB | | 2286 | 519277.48
5 | -> Vector Sort | [401.779, 503.698] | 160 | 54288 | | [1MB, 2MB] | 16MB | [1452,2091] | 2286 | 519285.94
6 | -> Vector Nest Loop Left Join (7, 8) | [337.447, 433.593] | 549662 | 54287 | | [4MB, 4MB] | 1MB | | 2286 | 519204.13
7 | -> CStore Scan on dmsalesw.dm_sale_risk_info_f t | [167.113, 257.594] | 549662 | 54287 | | [14MB, 14MB] | 1MB | | 2278 | 263009.89
8 | -> Vector Materialize | [107.275, 111.387] | 549678 | 16 | | [272KB, 272KB] | 16MB | [9,9] | 8 | 256134.87
9 | -> Vector Aggregate | [101.879, 105.931] | 16 | 16 | | [272KB, 272KB] | 1MB | | 16 | 256134.86
10 | -> Vector Streaming(type: BROADCAST) | [101.802, 105.876] | 256 | 256 | | [264KB, 264KB] | 2MB | | 16 | 256134.86
11 | -> Vector Aggregate | [38.193, 62.769] | 16 | 16 | | [272KB, 272KB] | 1MB | | 16 | 256134.73
12 | -> Vector Hash Semi Join (13, 17) | [37.799, 62.347] | 549662 | 54287 | | [196KB, 196KB] | 16MB | | 8 | 256126.23
13 | -> Vector Hash Semi Join (14, 15) | [36.000, 60.559] | 549662 | 57121 | 1 | [324KB, 324KB] | 16MB | | 14 | 256077.55
14 | -> CStore Scan on dmsalesw.dm_sale_risk_info_f t | [34.545, 59.081] | 549665 | 566022 | 11 | [6MB, 6MB] | 1MB | | 20 | 255952.73
15 | -> Vector Adapter | [0.005, 0.010] | 176 | 176 | 11 | [40KB, 40KB] | 1MB | [26,26] | 32 | 0.14
16 | -> Values Scan on "*VALUES*" | [0.001, 0.004] | 176 | 176 | | [16KB, 16KB] | 1MB | | 32 | 0.14
17 | -> Vector Adapter | [0.006, 0.008] | 80 | 80 | 5 | [40KB, 40KB] | 1MB | [26,26] | 32 | 0.06
18 | -> Values Scan on "*VALUES*" | [0.002, 0.005] | 80 | 80 | | [16KB, 16KB] | 1MB | | 32 | 0.06
从PERFORMANCE执行计划可以看到,SQL的运行时间从7s+缩减到了600ms-。可见避免在单个DN上执行SQL对单点性能的提升非常可观。
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)