对自定义函数开启parallel_enable属性使函数可以并行来提升SQL查询性能
Posted robinson1988
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对自定义函数开启parallel_enable属性使函数可以并行来提升SQL查询性能相关的知识,希望对你有一定的参考价值。
***人社系统最近做了数据迁移,采用国产的Zdata一体机替换了老旧的小型机,数据库也从11g升级为了19c
之前整个系统被拆分为5套子系统,这次升级将5套子系统做了整合,采用pdb的方式将5套子系统统一存放在Zdata一体机中
完成数据迁移之后,在没有针对SQL进行专门的优化的前提下,从各项指标上看,系统的整体性能提升了15-20倍
虽然整个系统的性能有了巨大提升,但是我们觉得还不够,我们的目标是将性能提升100倍
前几天对系统中一个老大难的SQL做了等价改写,SQL从每次执行5到10分钟降低为了15秒
今天继续优化跑得比较慢的存储过程,下面是从存储过程中抓出的缓慢SQL,每次执行要跑738-800秒
SELECT round(sum(decode(ac83.aaa036,
'100012',
pkg_B_Comm.Fun_GetAae019ByAaa088(ac83.aaa088,
ac83.aae019))) /
10000,
2),
round(sum(decode(ac83.aaa036,
'100500',
pkg_B_Comm.Fun_GetAae019ByAaa088(ac83.aaa088,
ac83.aae019))) /
10000,
2)
FROM ic10, ab01, ac01, ac82, ac83
WHERE ic10.aab001 = ab01.aab001
AND ic10.aac001 = ac01.aac001
AND ic10.aac001 = ac82.aac001
AND ic10.aae140 = ac82.aae140
AND ic10.aaa027 = ac82.aaa027
AND ac82.aaz220 = ac83.aaz220
AND AC82.AAE117 <> '4'
AND AC83.AAA036 IN ('100012', '100500')
AND ic10.aae140 = '110'
AND ac82.aae002 >= 202001
AND ac82.aae002 <= 202012
AND ac83.aae003 >= 202001
AND ac83.aae003 <= 202012
AND ac83.aae001 = 2020
AND ic10.aaa027 = '519900'
AND NVL(AB01.YAB019, '01') in ('01')
AND NVL(AB01.AAB019, '99') IN ('10')
AND NVL(AB01.AAB020, '900') IN ('110');
执行计划如下:
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 3793786172
------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 162 | | 1956K (1)| 00:01:17 | | |
| 1 | SORT AGGREGATE | | 1 | 162 | | | | | |
|* 2 | HASH JOIN | | 1280K| 197M| 90M| 1956K (1)| 00:01:17 | | |
|* 3 | HASH JOIN | | 739K| 81M| 21M| 374K (1)| 00:00:15 | | |
|* 4 | HASH JOIN | | 286K| 18M| 18M| 82831 (1)| 00:00:04 | | |
|* 5 | HASH JOIN | | 286K| 15M| | 31824 (1)| 00:00:02 | | |
|* 6 | MAT_VIEW ACCESS FULL| AB01 | 15060 | 308K| | 8313 (1)| 00:00:01 | | |
|* 7 | TABLE ACCESS FULL | IC10 | 527K| 17M| | 23509 (1)| 00:00:01 | | |
| 8 | INDEX FAST FULL SCAN | SYS_C_SNAP$_26083PK_AC01 | 26M| 276M| | 21309 (1)| 00:00:01 | | |
| 9 | PARTITION RANGE SINGLE| | 5067K| 241M| | 275K (1)| 00:00:11 | 12 | 12 |
|* 10 | TABLE ACCESS FULL | AC82 | 5067K| 241M| | 275K (1)| 00:00:11 | 12 | 12 |
| 11 | PARTITION RANGE SINGLE | | 38M| 1672M| | 1473K (1)| 00:00:58 | 12 | 12 |
|* 12 | TABLE ACCESS FULL | AC83 | 38M| 1672M| | 1473K (1)| 00:00:58 | 12 | 12 |
------------------------------------------------------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
---------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("AC82"."AAZ220"="AC83"."AAZ220")
3 - access("IC10"."AAC001"="AC82"."AAC001" AND "IC10"."AAE140"="AC82"."AAE140" AND "IC10"."AAA027"="AC82"."AAA027")
4 - access("IC10"."AAC001"="AC01"."AAC001")
5 - access("IC10"."AAB001"="AB01"."AAB001")
6 - filter(NVL("AB01"."AAB020",'900')='110' AND NVL("AB01"."AAB019",'99')='10' AND NVL("AB01"."YAB019",'01')='01')
7 - filter("IC10"."AAA027"='519900' AND "IC10"."AAE140"='110')
10 - filter("AC82"."AAA027"='519900' AND "AC82"."AAE117"<>'4' AND "AC82"."AAE140"='110' AND "AC82"."AAE002"<=202012)
12 - filter(("AC83"."AAA036"='100012' OR "AC83"."AAA036"='100500') AND "AC83"."AAE003">=202001 AND
"AC83"."AAE003"<=202012 AND "AC83"."AAE001"=2020)
Note
-----
- this is an adaptive plan
PARTITION RANGE SINGLE 表示做了分区裁剪,MAT_VIEW ACCESS FULL 表示走了物化视图全表扫描,这个执行计划没有问题
该存储过程是一个OLAP业务,对于OLAP的优化,通常采用 分区+并行+表垂直拆分 等手段进行优化
因为做了分区裁剪,也走了物化视图,所以分区这一招 我们就不用做了(已经做了)
现在开启并行看一下SQL查询速度
SELECT /*+ parallel(4) */ round(sum(decode(ac83.aaa036,
'100012',
pkg_B_Comm.Fun_GetAae019ByAaa088(ac83.aaa088,
ac83.aae019))) /
10000,
2),
round(sum(decode(ac83.aaa036,
'100500',
pkg_B_Comm.Fun_GetAae019ByAaa088(ac83.aaa088,
ac83.aae019))) /
10000,
2)
FROM ic10, ab01, ac01, ac82, ac83
WHERE ic10.aab001 = ab01.aab001
AND ic10.aac001 = ac01.aac001
AND ic10.aac001 = ac82.aac001
AND ic10.aae140 = ac82.aae140
AND ic10.aaa027 = ac82.aaa027
AND ac82.aaz220 = ac83.aaz220
AND AC82.AAE117 <> '4'
AND AC83.AAA036 IN ('100012', '100500')
AND ic10.aae140 = '110'
AND ac82.aae002 >= 202001
AND ac82.aae002 <= 202012
AND ac83.aae003 >= 202001
AND ac83.aae003 <= 202012
AND ac83.aae001 = 2020
AND ic10.aaa027 = '519900'
AND NVL(AB01.YAB019, '01') in ('01')
AND NVL(AB01.AAB019, '99') IN ('10')
AND NVL(AB01.AAB020, '900') IN ('110');
发现跑了1分钟没出结果,赶紧停掉,瞄了一眼代码,这个代码有自定义函数pkg_B_Comm.Fun_GetAae019ByAaa088
将自定义函数部分去掉,开并行查看速度 --- 17秒
pkg_B_Comm.Fun_GetAae019ByAaa088的代码如下:
function SCSB01.Fun_GetAae019ByAaa088(prm_aaa088 in varchar2, --应付类型
prm_aae019 in number --原金额
)
return number is
num_aae019 number(14, 2);
begin
if prm_aaa088 in ('03', '06', '09', '11') then
--特殊扣发、调整扣发、退发收款、正常扣发
num_aae019 := -nvl(prm_aae019, 0);
else
num_aae019 := nvl(prm_aae019, 0);
end if;
return num_aae019;
end Fun_GetAae019ByAaa088;
这个自定义函数挺简单的,就一个if判断,没有对其他表进行查询
像这种简单的自定义函数,一般是不太会影响SQL性能的(有,SQL引擎与PLSQL引擎上下文切换,但是影响不太大)
现在定位到是因为自定义函数导致开并行也跑得慢,有两个解决办法:
1. 将函数代码改写到SQL中,不去调用函数
2. 创建函数的时候,启用parallel_enable,让函数也可以开并行
我这里选择的是第二个方案,创建了一个测试函数,对函数开启parallel_enable
create or replace function SCSB01.test_fun_getaae019byaaa088(prm_aaa088 in varchar2, --应付类型
prm_aae019 in number --原金额
)
return number parallel_enable is
num_aae019 number(14, 2);
begin
if prm_aaa088 in ('03', '06', '09', '11') then
--特殊扣发、调整扣发、退发收款、正常扣发
num_aae019 := -nvl(prm_aae019, 0);
else
num_aae019 := nvl(prm_aae019, 0);
end if;
return num_aae019;
end;
最终SQL可以20秒跑完
SELECT /*+ parallel(4) */ round(sum(decode(ac83.aaa036,
'100012',
test_fun_getaae019byaaa088(ac83.aaa088,
ac83.aae019))) /
10000,
2),
round(sum(decode(ac83.aaa036,
'100500',
test_fun_getaae019byaaa088(ac83.aaa088,
ac83.aae019))) /
10000,
2)
FROM ic10, ab01, ac01, ac82, ac83
WHERE ic10.aab001 = ab01.aab001
AND ic10.aac001 = ac01.aac001
AND ic10.aac001 = ac82.aac001
AND ic10.aae140 = ac82.aae140
AND ic10.aaa027 = ac82.aaa027
AND ac82.aaz220 = ac83.aaz220
AND AC82.AAE117 <> '4'
AND AC83.AAA036 IN ('100012', '100500')
AND ic10.aae140 = '110'
AND ac82.aae002 >= 202001
AND ac82.aae002 <= 202012
AND ac83.aae003 >= 202001
AND ac83.aae003 <= 202012
AND ac83.aae001 = 2020
AND ic10.aaa027 = '519900'
AND NVL(AB01.YAB019, '01') in ('01')
AND NVL(AB01.AAB019, '99') IN ('10')
AND NVL(AB01.AAB020, '900') IN ('110');
如果还想对SQL进行优化,其实也可以,注意观察SQL语句的SELECT列,只访问极少列
可以对表添加组合索引走IFFS,或者对表做垂直拆分,但是现在已经20秒了,那就这样吧
以上是关于对自定义函数开启parallel_enable属性使函数可以并行来提升SQL查询性能的主要内容,如果未能解决你的问题,请参考以下文章
在Objective-C中按一个属性对自定义对象的NSSet进行排序