金蝶云星空,系统使用 Tmp 这类临时性质表的一点弊端及改进思路
ORACLE 11204
1、金蝶云星空企业版,在运行时,会使用大量的临时表来临时保存数据用做过渡,表名类似:TMPF067C8CE2DCE11EFB6E9000C29D(TMP开头,由数字和字母组成,长度达30位),这样命名只是为了确保表名唯一,用完后很快被删除,最长也不超过24小时就被删掉(被数据库的定时作业清除掉)。
2、显然,由于生命周期太短,这类表的数据若有什么特殊属性,数据库的优化器,事先无法预知。事后也无法弥补;而恰恰是缺少这类属性,导致优化器,在遇上这类表的一些特殊过滤条件时,无法获取准确的执行计划,影响了执行效率,
如下案例:
--2.1、语句的执行耗时情况:跑了34分钟没完成,
--2.2、执行计划,可以看出,预估值=10,与实际值13k(至少),是导致语句执行计划不佳的原因,
--2.3、引发预估行数和实际行数相差甚远的原因(过滤条件,T0是TMP表的别名)。
--2.4、原始语句,
--2.5、分析原因,
显然,是源码中的这个条件:T0.FJOINTYPE = 'OUTBILL' AND T0.FTYPE != 'STRAIGHT',让优化器对返回值,做了错误的预判,比如:T0.FJOINTYPE = 'OUTBILL'占表的总行数是0.002,T0.FTYPE != 'STRAIGHT'占表的总行数是0.92,若没有这两列的联合列统计信息,那优化器在计算此条件的返回行数时,会认为是:0.002*0.92=0.00184,但若两字段间的数据存在关系,很可能,满足此条件的实际行数,占总行数的0.5,此时,需要对这两列作联合列的统计信息采集,优化器才能获得较准确的预估值,,,
--2.6、分析原因2,
本来,若是普通的业务表,我们可以对这两字段,做联合列的统计信息采集,如下命令:exec dbms_stats.gather_table_stats(user,’表名’,method_opt=>’for columns(FJOINTYPE,FTYPE)’);,采集两字段联合后的统计信息,后续优化器再次遇上此语句时,便能够大致预估出此过滤条件后的行数,,,但由于是临时属性的TMP表,事先不知道表名,无法做有针对性的设置;事后这表又很快被删除,该表不再被使用,因此,优化器无法对它做两字段联合列的统计信息采集,,,
--2.7、系统当前对TMP表类型的表,做统计信息采集的策略,
由于TMP表只是一次性使用的临时表,在采集该表的统计信息时,我们采用了对所有字段都采集254桶柱状图的策略,以尽可能提供详尽的统计信息供优化器使用。显然,像上述语句中的这条件,T0.FJOINTYPE = 'OUTBILL' AND T0.FTYPE != 'STRAIGHT',优化器无法预判其会返回多少数据。
--2.8、 思考优化、改进策略,
一开始,我建议领域的同事,对原始T0表,执行这过滤条件:T0.FJOINTYPE = 'OUTBILL' AND T0.FTYPE != 'STRAIGHT',得到过滤的数据,生成新的T02表,然后用T02表,替代T0表,执行后续的UPDATE操作。当然,此时的UPDATE语句,要去掉这条件:T0.FJOINTYPE = 'OUTBILL' AND T0.FTYPE != 'STRAIGHT'。
但同事表示,原始的T0表中的数据,后续可能会用上,不能在此被排除掉。他提议,可以新增一个TMP表T02,保存这过滤后的数据,然后把这T02添加到语句中,和T0表关联,来过滤T0表中要被UPDATE的数据,也就是,通过T02表的数据,过滤/限制T0表的数据,代码大致如下:
由于是 EXISTS 关联关系,优化器能够准确预估此执行条件后的返回数,可以预见效果将比原先困扰优化器的写法:AND T0.FJOINTYPE = 'OUTBILL' AND T0.FTYPE != 'STRAIGHT',要好得多。若改成MERGE写法,只需要走一遍 WHERE EXISTS,效率会更好,
撰写此文的目的,是想共享此经验,开阔思维。
金蝶云星空,系统使用 Tmp 这类临时性质表的一点弊端及改进思路
本文2024-09-23 01:16:02发表“云星空知识”栏目。
本文链接:https://wenku.my7c.com/article/kingdee-k3cloud-144853.html