设为首页 收藏本站
查看: 974|回复: 0

[经验分享] SAP+DB2 糟糕的报表查询『ZCOR0015』 优化全程记录

[复制链接]

尚未签到

发表于 2015-9-17 11:27:11 | 显示全部楼层 |阅读模式
ZCOR0015的优化全过程记录文档







    2015年3月,今天无意翻到这篇写于2010年7月的文档,回想那时的工作,毕业3年初出茅庐的我面对接触不多的SAP+DB2竟敢操刀动斧,自信满满。
虽然这过程一路坎坷,数次判断几乎全都被打脸验证,看着如小强般坚毅的我,哈哈~ 文档里带着情绪的措词十分逗笑,可以洞悉那时的心情。
——如今5年过去,却久未闻硝烟,怀念ing~
  


目录:
ZCOR0015的优化全过程记录文档... 1
内容说明:1
1.寻找问题点,切入分析... 2
2.再次碰到问题,转折点.. 6
3.反复尝试,直至最后解决... 9
4.结果... 12
5.总结... 14
6.参考文档... 15


内容说明:

红色字体为突出个别文字的内容
黑色字体为普通过程阐述
蓝色字体为图解说明
黑色背景的字体为一些关键步骤点

优化目的:让ZCOR0015能够迅速取出数据,并且不通过MKPFMSEG中间表的处理.
业务的接受度: 跑1个月跨度的报表,要在5分钟内返回结果,跑半年的报表,要在1小时内返回结果.

结果: ZCOR0015 统计一个月的数据20090101~20090131
DSC0000.jpg

DSC0001.jpg

X轴为消耗时间,单位(秒)
上图为优化后的时间消耗,下图为优化前的时间消耗.
红色为数据库层时间消耗,蓝色为ABAP层时间消耗.

1.寻找问题根源,切入分析

先在se38里找出该程序,根据开发人员的测试,该程序的大部分的时间消耗在数据库层面,过后开发人员做了一个定时job,定期把视图WB2_V_MKPF_MSEG2的数据导入MKPFMSEG表里, 写程序的时候就直接从MKPFMSEG里取数据即可,这样暂时可以解决这个程序的缓慢问题,但是却把性能的隐患转移至后台job程序上了, 当前这个job跑起来非常消耗资源,并且也存在数据实时性的问题,
这次的调优目的就是彻底的解决这个问题.
我一开始的假想,认为这性能的问题,无非就是SQL语句所走的执行计划匹配上不合理的索引,导致不合理的资源消耗,当然这个早早下的结论也没错,只是太宽泛了,起初我认为折腾一天就可以搞定,甚至是一下午,但是最终却消耗了10天的时间…
没有全盘了解系统的细节结构、应用层面的逻辑 对SAP经验不足, 却又自信满满的姿态去看这次的问题 ,其实还蛮可怕的 ,这次的事情 所覆盖的知识面完全超出了我的想象。
把这段存在性能问题的sql从abap程序里抓出来,基本如下:



SAP端:
select a~matnr_i
a~mblnr_i
a~bwart_i
* b~aufnr
a~budat
a~WERKS_I
a~bukrs_i
a~menge_i
a~mjahr_i
a~aufnr_i
a~SHKZG_I
INTO CORRESPONDING FIELDS OF TABLE g_t_result
from WB2_V_MKPF_MSEG2 as a "inner join aufm as b on a~mblnr_i = b~mblnr and a~mjahr_i = b~mjahr and a~matnr_i = b~matnr
where
a~budat BETWEEN s_budat1 AND s_budat2 and
a~bukrs_i = s_bukrs and
a~werks_i = s_werks and
a~mjahr_i = s_mjahr and
a~bwart_i in ('101','102','122','123','161','162','Z09','Z10','Z11','Z12','Z21','Z22','Z01','Z02','601','602','653','654','261','262','531','532','543','544','961','962') AND
a~matnr_i BETWEEN '000000000010000000' and '000000000019999999'.
  
改成DB2里的SQL: (先暂时改成count(*) 计数,优化完了再改为实际取的字段名,大体上执行计划不会有差异.)



    select count(*)
from sapprd."MKPF" T0001, sapprd."MSEG" T0002
where
T0002."MANDT"= T0001."MANDT"
AND T0002."MBLNR"= T0001."MBLNR"
AND T0002."MJAHR"= T0001."MJAHR"
AND T0001."BUDAT" BETWEEN '20090101' AND '20090131'and
T0002."BUKRS"='1000'and
T0002."WERKS"='1000'and
T0001."MJAHR"='2009'and
T0002."BWART"in('101','102','122','123','161','162','Z09','Z10','Z11','Z12','Z21','Z22','Z01','Z02',
'601','602','653','654','261','262','531','532','543','544','961','962') AND
T0002."MATNR" BETWEEN '000000000010000000'and'000000000019999999';
执行计划如下,MKPF上的索引是非常不合理的,光是扫描一个索引就消耗那么多的资源,从这里就判定出索引的不合理性.
DSC0002.jpg
下面为这MSEG,MKPF这2张表在SDX里的状况,行数,统计信息更新时间,页数等等.与生产机的数据量基本同属于一个数量级.
第二段是MKPF的两个索引状况,叶子数,索引字段的基数,统计信息时间.(还有levels 一般不超过3)
DSC0003.jpg
以下为这个执行计划消耗的I/O ,CPU,BUFFERS等等资源的状况. (这里为估算值,并非真正的实际值)
DSC0004.jpg
接着开始在DB2数据库层面优化SQL.
单纯在数据库层面其实不复杂,就那么几个招式:
1.改SQL.
2.建立合适的索引(根据业务逻辑+需要把多少工作交给ABAP层面来做,减少SQL的谓词来取一个平衡点)
3.收集统计信息,让数据库有稳定的执行计划.

把SQL改写了一下



  • T0002."BUKRS"='1000'and
  • T0002."WERKS"='1000'and
  • T0001."MJAHR"='2009'and
  • T0002."BWART"in('101','102','122','123','161','162','Z09','Z10','Z11','Z12','Z21','Z22','Z01','Z02',
  • '601','602','653','654','261','262','531','532','543','544','961','962')

把上面这部分繁琐的业务逻辑去掉,如果把这部分内容纳入SQL里的话,会明显的加重取回数据的负担,将会扫描更多的索引页,或是建立更多的索引,本身SAP的内表就很强大,把这部分逻辑交给ABAP层面做即可,先取回数据到内表里,再LOOP循环一次,把结果筛选一遍,这样的思路应该是最优的.
然后再分别建两个索引.
附上建立的ddl:



    CREATE INDEX "SAPPRD"."IX_MKPF_BUDATMANDT"
ON "SAPPRD"."MKPF"
("BUDAT" ASC,
"MANDT" ASC
)
PCTFREE 10
DISALLOW REVERSE SCANS
COLLECT DETAILED STATISTICS;
GRANT CONTROL ON INDEX "SAPPRD"."IX_MKPF_BUDATMANDT" TO USER "SAPPRD";
CREATE INDEX "SAPPRD"."MSEG~GM"
ON "SAPPRD"."MSEG"
("MANDT" ASC,
"MBLNR" ASC,
"MJAHR" ASC,
"MATNR" ASC
)
PCTFREE 10
DISALLOW REVERSE SCANS
COLLECT DETAILED STATISTICS;
GRANT CONTROL ON INDEX "SAPPRD"." MSEG~GM " TO USER "SAPPRD";
  

大致上解释下这几个参数,
PCTFREE:为填充因子,10的含义是 遗留下10%的页空间,以供insert or update时不至于那么快导致页分裂,如果值太大的话,会导致扫描更多的索引页,增加成本.
DISALLOW REVERSE SCANS: 是否使用反转索引,其实在这是物理上的概念,并非索引就倒序了,只是避免热点块的发生,我们的SAP不存在那么高的并发,所以DISALLOW即可.
COLLECT DETAILED STATISTICS: 收集详细的统计信息.
接着在DB2里执行一下这个SQL:



    select
T0002.matnr,
T0001.mblnr,
T0002.bwart,
T0001.budat,
T0002.WERKS,
T0002.bukrs,
T0002.menge,
T0001.mjahr,
T0002.aufnr,
T0002.SHKZG
from sapprd."MKPF" T0001 inner join sapprd."MSEG" T0002 on
T0002."MANDT"= T0001."MANDT"
AND T0002."MBLNR"= T0001."MBLNR"
AND T0002."MJAHR"= T0001."MJAHR"
where T0001."BUDAT" BETWEEN '20090101' AND '20090201'and
T0002."MATNR" BETWEEN '000000000010000000'and'000000000019999999';
  
DSC0005.jpg

速度不错,两次索引扫描,然后fetch 出数据,再做Nested loop join,
并且先loop的小表MKPF,成本都很低,这已经是最佳的执行计划以及索引选择了,这个时候我笑了,我认为问题已经完美解决,并且返回数据的速度都在个位数的秒级.

2.再次碰到问题,转折点.

这时我把程序交给开发人员,我把思路说明了一遍,我说这个程序现在1个月数据量在10秒内即可出来,半年的数据量顶天也就1分钟,
等开发人员把程序写入ABAP报表里了,跑了一下,我傻眼了....一个月的数据量, 竟然需要10+分钟...和我预期的完全不一样.
脑袋里开始空白了,这是过于自信所带来的空白..

开始扫荡式寻找问题点,数据库性能,数据库参数,AIX性能,磁盘
存储 网络,ABAP代码..全都翻了一遍,
起初发现AIX里的TOPAS显示

hdisk4
的busy%程度非常高,高达90+,但是CPU显示的user不高,大部分CPU时间都是在wait,IDLE基本持平90%左右 (这时SDX的环境只有我一个人在用)
(tips:后来事情结束后才恍然大悟,其实大量表扫描+hash join的特征.)

虽然我脑子里很清晰的知道,他们出问题的可能性不大,
其实这时我心里很清楚,由于我自身的瓶颈(SAP+DB2的经验不足)其间的逻辑交互机制不清晰,单方面从数据库层面来判断问题,显然是捉襟见肘的。
但是近乎自负的那种自信却抵触了我否定自己的念头。

接着还是一味的在AIX操作系统层面寻找问题…… 并且还消耗了蛮多时间,索性到最后,还是回到数据库,啃了些文档,打算在数据库层面寻找问题点。



  • select*from sysibmadm.TOP_DYNAMIC_SQL order by AVERAGE_EXECUTION_TIME_S desc fetch first 5 rows only;



上面的语句可以找出在共享池里的SQL,按执行时间排序.(这个手段我在Oracle和SQLServer里倒是经常用,但是DB2比较陌生..)
发现了从ABAP层送出的SQL语句和我所认知的完全不一样,这个时候开始有突破口了.
下面的代码为ABAP层面送给DB2的原句
SELECT T_00."MATNR",T_01."MBLNR",T_00."BWART",T_01."BUDAT",
                                 T_00."WERKS",T_00."BUKRS",T_00."MENGE",T_01."MJAHR",
                                                T_00."AUFNR",T_00."SHKZG"
FROM SAPPRD."MSEG"T_00 INNER JOIN SAPPRD."MKPF"T_01 ON
                            T_01."MANDT"=?
                            AND T_01."MANDT"=T_00."MANDT"
                            AND T_01."MBLNR"=T_00."MBLNR"
                            AND T_01."MJAHR"=T_00."MJAHR"
WHERE
T_00."MANDT"=? AND
T_01."BUDAT" BETWEEN ? AND ? AND
T_00."MATNR" BETWEEN ? AND ?  
WITH UR -- OPTLEVEL( 10 ) -- QUERY_DEGREE( 1 )
-- LOCATION( ZCOR0015_1 , 125 ) -- SYSTEM( SDX , SAPPRD )'


DSC0006.jpg
看了下执行计划,终于找到问题了,
ABAP层面送出的SQL语句,所导致的执行计划是非常烂的,又走回之前的2个索引,一个MKPF~BUD,一个是MSEG~M,
并且根据这个索引计算出来的连接方式为HASH JOIN. 使用不合理的索引,带来的成本是非常高的.

这段程序和我所想传送给数据库的语句有2个不同的地方
首先是T_01."MANDT"=?

其次是WITH UR --
OPTLEVEL( 10 ) -- QUERY_DEGREE( 1 ) -- LOCATION( ZCOR0015_1 , 125 ) -- SYSTEM(
SDX , SAPPRD )'


琢磨了下 MANDT这个是cline代码,用来区分800,900的数据,SAP自动给加上的,可以理解
再者是这个WITH UR 是锁级别,
OPTLEVEL(10)是优化级别,QUERY_DEGREE是并行的参数.
为什么带这些东西后,这段程序的执行计划差异会那么大呢??

我把『?』问号 都替换成了实际值,然后套入SQL里,执行下面的语句.
而且还在我后来建立的MKPF索引里添加了MANDT字段,
SELECT T_00."MATNR",T_01."MBLNR",T_00."BWART",T_01."BUDAT",
                                 T_00."WERKS",T_00."BUKRS",T_00."MENGE",T_01."MJAHR",
                                                T_00."AUFNR",T_00."SHKZG"
FROM SAPPRD."MSEG"T_00 INNER JOIN SAPPRD."MKPF"T_01 ON
                            T_01."MANDT"='900'
                            AND T_01."MANDT"=T_00."MANDT"
                            AND T_01."MBLNR"=T_00."MBLNR"
                            AND T_01."MJAHR"=T_00."MJAHR"
WHERE
T_00."MANDT"='900' AND
T_01."BUDAT" BETWEEN '20090101' AND '20090201' AND
T_00."MATNR" BETWEEN '000000000010000000' AND '000000000019999999'
WITH UR -- OPTLEVEL( 10 ) -- QUERY_DEGREE( 1 )
-- LOCATION( ZCOR0015_1 , 125 ) -- SYSTEM( SDX , SAPPRD )'

DSC0007.jpg
这样子速度非常快,并且执行计划也是正确的,我就纳闷了.为什么同样的程序,我提交给DB2的 与ABAP提交给DB2的相差那么?
脑子再次迷糊.

3.反复尝试,直至最后解决

这时我想到了用HINT来解决问题,在ABAP里写HINT 强制走NESTED LOOP,指定索引

尝试了一大堆%_HINT未果,仍然找不到匹配DB2使用的HINT
就连网上比较普遍的一个HINT也无果,没有实际作用,
%_HINTS
    ADABAS   'ORDERED'
    INFORMIX 'ORDERED'
    MSSQLNT  'OPTION FORCE ORDER'                        
    DB6 '<NLJOIN><IXSCAN TABLE=''MSEG'' SAP_INDEX=''M''/>'
    DB6 '<IXSCAN TABLE=''MKPF'' SAP_INDEX=''0''/></NLJOIN>'.

我猜测或许SAP针对DB2这一块写的HINT程序,只是匹配的早前的DB2版本,在后续版本中已经废弃掉了,
DSC0008.jpg
(我之所以会这么猜测,是因为我跟踪了MB51的标准程序,有一个选择用过账日期开始来访问数据库,但是这个标准程序根本就不会走到那段HINT代码里,说明这个选择是不会生效的)
忽然有种被忽悠的感觉.
DSC0009.jpg
如果废弃了,应该有所说明,并且把新的语法公告之,但是我没有翻到相关的ABAP基于DB2 HINT的说明文档,这种undocument的方式让人十分不爽啊.
反倒是Oracle的HINT说明非常多,而且很容易使用,我在这里非常的BS IBM DB2,它让我翻看了一晚的程序,翻文档
翻到有一种想吐的感觉..
Google搜索”ABAP DB2 HINT” 这三个关键字,能搜索到的内容我全都尝试了一遍.
我已经快2年没有这样的感觉了,真恶心啊。

下面这个也是模仿着Oracle写的,没法使用,在ABAP里写Oracle的HINT就很容易,因为语法和Oracle里的基本一样,对比之下Oracle的文档就丰富许多.(黝黑18摸)
*%_HINTS DB2 'USE VALUES FOR OPTIMIZATION'
*DB2 'INDEX("MKPF" "MKPF~BUD")'.

SAP的F1 文档很烂,在这一块基本没什么说明,如此大的几家厂商,一起融洽的合作就那么困难吗??稍微麻烦的点的东西就undocument
,太让人气愤..这个时候我甚至想到把问题发msg给SAP.
多次测试之后,以瞎猫碰上死老鼠的方式…
发现USE_OPLEVEL这个参数起到了效果,原来是
WITH UR -- OPTLEVEL( 10 )
-- QUERY_DEGREE( 1 ) -- LOCATION( ZCOR0015_1 , 125 ) -- SYSTEM( SDX , SAPPRD )'

这个10级的OPTLEVEL,起到了影响.

这个参数是可以对应到DB2里的.不明白为什么SAP默认要强制送给DB2这个参数??而且SAP默认安装的时候DB2数据库的这个参数值为5 ,也不知道从哪里可以改变SAP的这个默认级别.
无论你在数据库层面更改这个值, ABAP程序送出SQL时总会是带上--
OPTLEVEL( 10 )
的 ,有点类似于 基于session更改参数的概念. 不影响整体参数.

这时快要水落石出了,解决它有2种途径:
1、更改&SUBSTITUTE VALUES&对于 SAP 内核版本
4.6,这个参数需要把 SAP 配置参数 dbs/db6/dbsl_substitute_literals
设置为 1 。这个参数使在 ABAP 语句中的所有输入值都被作为 SQL 语句文本的文字实现。就像 &SUBSTITUTE LITERALS&,DB2 优化器可以使用关于涉及表中的分布信息。这个基本同等于Oracle理解的绑定变量,如果设置了这个值的话,SAP送给DB2的SQL就是完整的,带有”字面值”的,那么我相信执行计划也一定会是正确的,不过修改这个SAP配置参数的评估已不在我目前的工作范围内了.

2、在ABAP程序里加上提示DB6 'USE_OPTLEVEL 0'.

我现在在DB2里测试下这个级别0,与10的区别

在数据库里对应这个SAP的参数是DFT_QUERYOPT

(在DB2里为0,1,2,3,5,7,9,但是不明白为什么SAP里有10级…咳咳,不知道这是怎么对应上的,反正只能靠猜测了):
在session端更改DFT_QUERYOPT(db cfg级别), 默认为5
譬如SQL:set current query optimization = 5

数据库里最大值为9,SAP里最大值为10.最小值都为0






    Set current query optimization =9;
SELECT T_00."MATNR", T_01."MBLNR", T_00."BWART", T_01."BUDAT",
T_00."WERKS", T_00."BUKRS", T_00."MENGE", T_01."MJAHR",
T_00."AUFNR", T_00."SHKZG"
FROM SAPPRD."MSEG" T_00 INNER JOIN SAPPRD."MKPF" T_01 ON
T_01."MANDT"=?
AND T_01."MANDT"= T_00."MANDT"
AND T_01."MBLNR"= T_00."MBLNR"
AND T_01."MJAHR"= T_00."MJAHR"
WHERE
T_00."MANDT"=? AND
T_01."BUDAT" BETWEEN ? AND ? AND
T_00."MATNR" BETWEEN ? AND ?
WITH UR;
  


以下为执行计划 DSC00010.png
烂的一塌糊涂,还是hash join, 而且也走的错误的索引,不是我想要的执行计划.
难怪ABAP送出的SQL与我相同,但是执行计划不同,导致性能并且很慢的原因就在这里.

改为0级别,再测试一次.
set current query optimization = 0;
DSC00011.png
这次的执行计划正确了!!


4.结果

在之前所创建的索引下,
套用到ABAP程序底部,加个HINT





    select
MSEG~matnr
MKPF~mblnr
MSEG~bwart
* b~aufnr
MKPF~budat
MSEG~WERKS
MSEG~bukrs
MSEG~menge
MKPF~mjahr
MSEG~aufnr
MSEG~SHKZG
INTO CORRESPONDING FIELDS OF TABLE g_t_result
from MSEG AS MSEG INNER JOIN MKPF as MKPF
on MKPF~MANDT = MSEG~MANDT
AND MKPF~MBLNR = MSEG~MBLNR
AND MKPF~MJAHR = MSEG~MJAHR
WHERE
* a~budat in s_budat AND
MKPF~BUDAT BETWEEN '20090101' AND '20090201'and
MSEG~MATNR BETWEEN '000000000010000000'and'000000000019999999'
%_HINTS DB6 'CONVERT_FAE_TO_CTE'
DB6 'USE_OPTLEVEL 0'.
  

其实让DB2查询优化器,自己分析出来的执行计划我还是很放心的,但是在SAP里解析后的程序,添油加醋后 再给DB2, 这是完全不同的东西,很让我失望
MKPF~BUDAT BETWEEN '20090101' AND '20090105'  and

       MSEG~MATNR BETWEEN '000000000010000000' and '000000000019999999'
调整过后的速度非常快,与我预料的一样,类似上面这样的5天范围的数据查询,把数据返回到内表里,连1秒钟都不需要.
MKPF~BUDAT BETWEEN '20090101' AND '20090201'  and

       MSEG~MATNR BETWEEN '000000000010000000' and '000000000019999999'
一个月的数据返回到内表里,大致5~10秒.
MKPF~BUDAT BETWEEN '20090101' AND '20090701'  and

       MSEG~MATNR BETWEEN '000000000010000000' and '000000000019999999'
半年的数据返回到内表,不到1分钟
我本来以为数据库层面的问题已经完全解决了,但是这里又再出出了问题,其实在测试阶段出问题倒挺好的,做到全面的测试,把问题都扼杀在测试阶段..省得到时候部署到生产系统之后再出乱子.
2010-7-29,今天发现程序变得异常缓慢,与昨天的不一样,我调整了下优化级别为0,然后到DB2里看看执行计划.





    set current query optimization =0;
SELECT T_00."MATNR", T_01."MBLNR", T_00."BWART", T_01."BUDAT",
T_00."WERKS", T_00."BUKRS", T_00."MENGE", T_01."MJAHR",
T_00."AUFNR", T_00."SHKZG"
FROM SAPPRD."MSEG" T_00 INNER JOIN SAPPRD."MKPF" T_01 ON
T_01."MANDT"=?
AND T_01."MANDT"= T_00."MANDT"
AND T_01."MBLNR"= T_00."MBLNR"
AND T_01."MJAHR"= T_00."MJAHR"
WHERE
T_00."MANDT"=? AND
T_01."BUDAT" BETWEEN ? AND ? AND
T_00."MATNR" BETWEEN ? AND ?
WITH UR;
  


DSC00012.png
真奇怪, 又用回MKPF~0索引了,真的有够烂,不稳定的执行计划是不可能放入生产环境使用的,看来还是要彻底的去解决这个问题.
假设 : 每次ABAP层面发送SQL语句给DB2的时候都是发送的字面值,而不是类似 “MANDT=?”这样的问号,那么这时数据库的CBO就会根据统计信息生成最优的执行计划。
(虽然每次发送字面值,都会让数据去解析执行计划,会造成执行计划不共享,带来一小部分CPU开销,但是我认为是值得的,因为是报表程序(OLAP应用),正确的执行计划显得非常重要,并且这个CPU开销是十分小的,对比这个报表的消耗时间来说,基本可以忽略掉了)
如果把ABAP对SQL语句造成的影响降到最低,那么就是发送字面值了(字面值的意思就是类似 “MANDT=900”这样的程序).

以下的公式是成立的:
SQL发送字面值+正确稳定的统计信息+合理的索引 &asymp; 最优的执行计划 &asymp; 最优的速度

这时候想办法让ABAP发送字面值吧
1.   开始想到了改 SAP 配置参数 dbs/db6/dbsl_substitute_literals 设置为 1 ,这样SAP系统发送的全部SQL语句都是字面值..
可惜我没找到相关的参数,网上的资料说这个参数是4.6版本的,新版本的找不到了,但是却可以找到对应Oracle和MSSQL的参数.(再次晕倒..)

2.   在ABAP程序里增加 %_HINT来强制 程序发送字面值,
WHERE
      MKPF~BUDAT BETWEEN '20090101' AND '20090201'  and
      MSEG~MATNR BETWEEN '000000000010000000' and '000000000019999999'
    %_HINTS
      DB6 'CONVERT_FAE_TO_CTE'
      DB6 'USE_OPTLEVEL 0'
      DB2 '&SUBSTITUTE LITERALS&'
      DB6 '&SUBSTITUTE LITERALS&'
      DB2 '&SUBSTITUTE VALUES&'
      DB6 '&SUBSTITUTE VALUES&'.
经过测试,程序速度很快 10秒内数据就返回到内表了 : ) 估计发送了字面值,DB2的执行计划已经正确,但是为了严谨,我还是要验证一下.
select * from sysibmadm.TOP_DYNAMIC_SQL where stmt_text like '% FROM
"MKPF" T_00 INNER JOIN "MSEG" T_01 %' order by AVERAGE_EXECUTION_TIME_S;
经查询,字面值已经传入数据库.这样子,执行计划就是正确的了!


DSC00013.jpg
只要保证ABAP报表程序传入的是字面值,并且数据库端的索引是合理的,并且数据库的统计信息是正确的话, 就会拥有稳定的执行计划,以及最优的执行效率!
至此数据库层面的优化基本结束,这才是数据库真正的威力。
接着放到测试环境里测试,发现ABAP程序里还是消耗了10分钟(这时数据库的消耗只有5秒),
进行跟踪发现瓶颈在2万多数据量的内表嵌套LOOP部分.导致的性能低下。
这时与吴恒取得联系,让他给予一些关于嵌套LOOP的优化思路.然后我们这边的开发人员迅速做出了响应,不断的更改测试,更改测试,最后取得了非常好的效果!!!
以下为开发人员做的一些优化记录,经过这段时间的折腾,我们的开发人员能力也得到了提升,懂得如何撰写性能良好的程序 : )
SAP系统 ABAP 层面的性能优化。
在sap中进行abap编程时候会不可避免的遇到循环嵌套,在本着尽量少的访问数据库时,将数据多放在内存中处理,提高数据的处理速度。一般不建议使用loop嵌套循环,特别是针对数据量大时,体现的尤为明显。尽量在通过READ TABLE来读取第二张内表中的数据,再进行数据逻辑的处理,这样子在取数性能上可以大大提高效率。
下面我们就拿取一个月的数据时,所用到的两个逻辑取数耗时上的对比:
一、使用LOOP循环嵌套取数。

1、语法:





    LOOP AT g_t_out.
LOOP AT g_t_result WHERE matnr = g_t_out-matnr_i .
IF g_t_result-bwart ='101'.
g_t_out-DDCHS = g_t_out-DDCHS + g_t_result-menge.
ELSEIF g_t_result-bwart ='102'.
g_t_out-CXDDS = g_t_out-CXDDS + g_t_result-menge.
ELSEIF g_t_result-bwart ='122'.
g_t_out-GYSTH = g_t_out-GYSTH + g_t_result-menge.
……………
ENDLOOP.
modify g_t_out.
CLEAR g_t_out.
ENDLOOP.

  

2、耗用时间(如下图):
DSC00014.jpg
通过 LOOP循环嵌套取09年1月份的数据耗时为784秒
二、使用read table… with key 取数。
1、语法:






    sort g_t_out by matnr_i auart budat mblnr_i.
sort g_t_collect by matnr_i auart budat mblnr.
LOOP AT g_t_out.
read table g_t_collect With key matnr_i = g_t_out-matnr_i bwart_i ='101' BINARY SEARCH.
if sy-subrc =0.
g_t_out-DDCHS = g_t_out-DDCHS + g_t_collect-menge_i.
endif.
read table g_t_collect With key matnr_i = g_t_out-matnr_i bwart_i ='102' BINARY SEARCH.
if sy-subrc =0.
g_t_out-CXDDS = g_t_out-CXDDS + g_t_collect-menge_i.
endif.
read table g_t_collect With key matnr_i = g_t_out-matnr_i bwart_i ='122' BINARY SEARCH.
if sy-subrc =0.
g_t_out-GYSTH = g_t_out-GYSTH + g_t_collect-menge_i.
endif.
modify g_t_out.
CLEAR g_t_out.
ENDLOOP.
  



2、耗用时间(如下图):
DSC00015.jpg
使用READ TABLE取09年1月份的数据耗时为19秒
通过以上两种方法进行比较,第一种方法取同一月的数据要784秒,而第二种取同一月的数据只要19秒。显然采用第二种方法取数在效率上要远远优于第一种方法。



5.总结

这次的调优的总体难度对比以往在MIS MES上的Oracle,SQLServer来说并不算很难(单纯考虑数据库层面的对比),
但是对SAP的一些内部机制不够了解,对DB2的陌生……导致消耗了大量的时间在反复测试和阅读文档上,
途中找瓶颈点时还折腾了AIX,怀疑操作系统配置,改动了SAP参数,数据库参数,怀疑存储性能,把所有可能的环节全都怀疑了一遍, 把熬工,刘工等人也折腾得不行了,ABAP开发人员也配合一起做了大量的更改+测试..

总体来讲,调优还是需要对整体应用系统涵盖数据库,后台操作系统,存储等等..有全面的了解
只有全方位的了解应用系统,以及数据库才能快速的定位出问题根源,
否则只能是铺天盖地的寻找瓶颈点头痛医头脚痛医脚,甚至像我一样到最后依靠瞎猫碰死老鼠的招式,不过能解决问题仍然是很高兴的,
与大家分享之 : )

6.参考文档

DB2 最佳实践: 编写并调优查询语句以优化性能最佳实践
http://www.ibm.com/developerworks/cn/data/library/techarticles/dm-0909querytuning/
SAP官方联机丛书
IBM DB2官方联机丛书

2010-7-28 @LDK xinyu

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-114894-1-1.html 上篇帖子: SAP 资料目录 下篇帖子: RFC访问SAP(C#)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表