注册
达梦执行计划中PRJT2操作符代价高优化思路分享
培训园地/ 文章详情 /

达梦执行计划中PRJT2操作符代价高优化思路分享

Paige 2023/03/06 1943 3 0

PRJT操作符介绍

Prjt2操作符,表示关系的“投影“计算,用于选择表达式的计算
附加的提示内容有“选择的表达式个数“和”是否要求单行数据“
该操作符代价高时,一般是select 后面的case when表达式、子查询表达式、用户自定义函数的表达式计算太慢

计划中PRJT操作符与sql的对应关系介绍

select 
case when a.subtype$='UTAB' then '用户表对象' when a.subtype$='VIEW' then '视图' else '其它' end as a,  --case when 表达式
(select name from SYS.SYSOBJECTS where id=a.schid) as b ,--子查询表达式
substr(name,1,3) as c ,--系统函数表达式
name
from SYS.SYSOBJECTS a
where a.type$ in('SCHOBJ');

explain 以上sql 可看到执行计划如下:

1   #NSET2: [1, 22, 148] 
2     #PIPE2: [1, 22, 148] 
3       #PRJT2: [1, 22, 148]; exp_num(4), is_atom(FALSE) 
4         #CSEK2: [1, 22, 148]; scan_type(ASC), SYSINDEXSYSOBJECTS(SYSOBJECTS as A), scan_range[('SCHOBJ',min,min),('SCHOBJ',max,max))
5       #SPL2: [1, 1, 52]; key_num(1), spool_num(0), is_atom(TRUE), has_var(1), sites(-)
6         #PRJT2: [1, 1, 52]; exp_num(1), is_atom(TRUE) 
7           #TOPN2: [1, 1, 52]; top_num(1)
8             #SSEK2: [1, 1, 52]; scan_type(ASC), SYSINDEXIDSYSOBJECTS(SYSOBJECTS), scan_range[var1,var1]

第3行prjt 对应的是最终查询需要呈现的表达式,**exp_num(4)**表示表达式个数是4
第6行prjt 对应的是查询项中对应的子查询表达式(对应的是sql语句部分
(select name from SYS.SYSOBJECTS where id=a.schid) as b ,–子查询表达式
要求是单行记录,所以 is_atom(TRUE)

Case when表达式慢

可借助ini参数CASE_WHEN_CVT_IFUN进行优化,主要根据其写法判断能不能走批量优化
image.png
以下sql执行et高在prjt2的case when 表达式上,由于case when的字段较多,可以分别屏蔽一些判断是哪些较慢,针对第一种case when的写法需要将case_when_cvt_ifun包含1和64才能走批量,即加hint/+case_when_cvt_ifun(65)/,针对第二种case when的写法将case_when_cvt_ifun包含1即可批量,即加hint/+case_when_cvt_ifun(1)/

image.png

子查询表达式慢

借助ini参数ENABLE_RQ_TO_NONREF_SPL进行优化,主要根据返回的结果集数据量来判断。
在返回结果集数量不多情况下一条条处理快;返回结果集数据量大,平坦化快。
默认0(平坦化)
image.png
怎么判断计划中是一条条处理还是平坦化处理的?看执行计划中SPL2操作符部分

例如以下查询sql部分对应的prj2代价高,高在红框中的相关子查询表达式

image.png

对应计划如下 prj2中可看到三元组 数据量估算有81161条记录,spl2 看到24行有传var,说明是一条条处理,这就意味着下面spl2部分要迭代执行81161次(bpq的cscn、slct、rnsk、prjt2),如果展开平坦化处理应该会更高效(bpq只需全扫一次),还有如果bpq表有person_id字段的索引,走seek定位,一条条处理应该也还好:

image.png

构造以下类似案例,供参考:

drop table if exists t1;
drop table if exists t2;
create table t1 as 
select level as a ,dbms_random.string('u',20)as name,level%10000 as b from dual connect by level<100000;
create table t2 as
select level as a ,dbms_random.string('u',20)as name,dbms_random.string('u',20)as tname,level%10000 as b from dual connect by level<10000;
--执行以下查询
select max(name)
,sum(b)
,sum(c)
from (
select   *
,(select name from t2 where a=a.b ) as pname
,(select b from t2 where a=a.b ) as c
from t1 a
)
;

Sql分析解说:sql中pname、c 子查询表达式,只有所有记录都算完之后,才可能算外层的求总,所以平坦化快,需要ENABLE_RQ_TO_NONREF_SPL(0)

当ENABLE_RQ_TO_NONREF_SPL=0时执行耗时、计划、ET

执行成功, 执行耗时48毫秒. 执行号:12509
image.png
image.png!

当ENABLE_RQ_TO_NONREF_SPL=1时执行耗时、计划、ET

执行成功, 执行耗时14秒 575毫秒. 执行号:12534
image.pngimage.png

函数表达式慢

这种一般只能采用select 项进行屏蔽该表达式后观察执行时间是否有很大提升,来进行辅助判断,需要进函数体定义里进行分析调优,将函数体的计划调到最优
说明:自定义函数一定要是确定性函数,才可能走批量,否则只能一条条处理

其它

更多操作符信息和ini参数的使用说明可参见达梦数据库说明文档 DBA手册
查询相关ini参数:
https://eco.dameng.com/document/dm/zh-cn/pm/physical-storage.html#2.1.1.1.5%20%E6%9F%A5%E8%AF%A2%E7%9B%B8%E5%85%B3
执行计划操作符说明文档:
https://eco.dameng.com/document/dm/zh-cn/pm/dm8-admin-manual-appendix.html#%E9%99%84%E5%BD%95%204%20%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92%E6%93%8D%E4%BD%9C%E7%AC%A6

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服