SAP+DB2 糟糕的報表查詢『ZCOR0015』 優化全程記錄


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

 

 

X軸為消耗時間,單位()

上圖為優化后的時間消耗,下圖為優化前的時間消耗.

紅色為數據庫層時間消耗,藍色ABAP層時間消耗.

1.尋找問題根源,切入分析

先在se38里找出該程序,根據開發人員的測試,該程序的大部分的時間消耗在數據庫層面,過后開發人員做了一個定時job,定期把視圖WB2_V_MKPF_MSEG2的數據導入MKPFMSEG, 寫程序的時候就直接從MKPFMSEG里取數據即可,這樣暫時可以解決這個程序的緩慢問題,但是卻把性能的隱患轉移至后台job程序上了, 當前這個job跑起來非常消耗資源,並且也存在數據實時性的問題,

這次的調優目的就是徹底的解決這個問題.

我一開始的假想,認為這性能的問題,無非就是SQL語句所走的執行計划匹配上不合理的索引,導致不合理的資源消耗,當然這個早早下的結論也沒錯,只是太寬泛了,起初我認為折騰一天就可以搞定,甚至是一下午,但是最終卻消耗了10天的時間

沒有全盤了解系統的細節結構、應用層面的邏輯 SAP經驗不足, 卻又自信滿滿的姿態去看這次的問題 ,其實還蠻可怕的 ,這次的事情 所覆蓋的知識面完全超出了我的想象。

把這段存在性能問題的sqlabap程序里抓出來,基本如下:

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上的索引是非常不合理的,光是掃描一個索引就消耗那么多的資源,從這里就判定出索引的不合理性.

下面為這MSEG,MKPF2張表在SDX里的狀況,行數,統計信息更新時間,頁數等等.與生產機的數據量基本同屬於一個數量級.

第二段是MKPF的兩個索引狀況,葉子數,索引字段的基數,統計信息時間.(還有levels 一般不超過3)

以下為這個執行計划消耗的I/O ,CPU,BUFFERS等等資源的狀況. (這里為估算值,並非真正的實際值)

接着開始在DB2數據庫層面優化SQL.

單純在數據庫層面其實不復雜,就那么幾個招式:

1.SQL.

2.建立合適的索引(根據業務邏輯+需要把多少工作交給ABAP層面來做,減少SQL的謂詞來取一個平衡點)

3.收集統計信息,讓數據庫有穩定的執行計划.

 

SQL改寫了一下

  1. T0002."BUKRS"='1000'and
  2. T0002."WERKS"='1000'and
  3. T0001."MJAHR"='2009'and
  4. T0002."BWART"in('101','102','122','123','161','162','Z09','Z10','Z11','Z12','Z21','Z22','Z01','Z02',
  5. '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';

 


速度不錯,兩次索引掃描,然后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操作系統層面尋找問題…… 並且還消耗了蠻多時間,索性到最后,還是回到數據庫啃了些文檔,打算在數據庫層面尋找問題點。

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

上面的語句可以找出在共享池里的SQL,按執行時間排序.(這個手段我在OracleSQLServer里倒是經常用,但是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 )'

 

看了下執行計划,終於找到問題了,

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 )'

這樣子速度非常快,並且執行計划也是正確的,我就納悶了.為什么同樣的程序,我提交給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版本,在后續版本中已經廢棄掉了,

(我之所以會這么猜測,是因為我跟蹤了MB51的標准程序,有一個選擇用過賬日期開始來訪問數據庫,但是這個標准程序根本就不會走到那段HINT代碼里,說明這個選擇是不會生效的)

忽然有種被忽悠的感覺.

如果廢棄了,應該有所說明,並且把新的語法公告之,但是我沒有翻到相關的ABAP基於DB2 HINT的說明文檔,這種undocument的方式讓人十分不爽啊.

反倒是OracleHINT說明非常多,而且很容易使用,我在這里非常的BS IBM DB2,它讓我翻看了一晚的程序,翻文檔 翻到有一種想吐的感覺..

Google搜索”ABAP DB2 HINT” 這三個關鍵字,能搜索到的內容我全都嘗試了一遍.

我已經快2年沒有這樣的感覺了,真惡心啊。

 

下面這個也是模仿着Oracle寫的,沒法使用,ABAP里寫OracleHINT就很容易,因為語法和Oracle里的基本一樣,對比之下Oracle的文檔就豐富許多.(黝黑18摸)

*%_HINTS DB2 'USE VALUES FOR OPTIMIZATION'
*DB2 'INDEX("MKPF" "MKPF~BUD")'.

 

SAPF1 文檔很爛,在這一塊基本沒什么說明,如此大的幾家廠商,一起融洽的合作就那么困難嗎??稍微麻煩的點的東西就undocument ,太讓人氣憤..這個時候我甚至想到把問題發msgSAP.

多次測試之后,以瞎貓碰上死老鼠的方式

發現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送給DB2SQL就是完整的,帶有字面值,那么我相信執行計划也一定會是正確的,不過修改這個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;

 

以下為執行計划

爛的一塌糊塗,還是hash join, 而且也走的錯誤的索引,不是我想要的執行計划.

難怪ABAP送出的SQL與我相同,但是執行計划不同,導致性能並且很慢的原因就在這里.

 

改為0級別,再測試一次.

set current query optimization = 0;

這次的執行計划正確了!!

 

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;

 

真奇怪, 又用回MKPF~0索引了,真的有夠爛,不穩定的執行計划是不可能放入生產環境使用的,看來還是要徹底的去解決這個問題.

假設 : 每次ABAP層面發送SQL語句給DB2的時候都是發送的字面值,而不是類似 “MANDT=?”這樣的問號,那么這時數據庫的CBO就會根據統計信息生成最優的執行計划。

(雖然每次發送字面值,都會讓數據去解析執行計划,會造成執行計划不共享,帶來一小部分CPU開銷,但是我認為是值得的,因為是報表程序(OLAP應用),正確的執行計划顯得非常重要,並且這個CPU開銷是十分小的,對比這個報表的消耗時間來說,基本可以忽略掉了)

如果把ABAPSQL語句造成的影響降到最低,那么就是發送字面值了(字面值的意思就是類似 “MANDT=900”這樣的程序).

 

以下的公式是成立的:

SQL發送字面值+正確穩定的統計信息+合理的索引 ≈ 最優的執行計划  最優的速度

 

這時候想辦法讓ABAP發送字面值吧

1.   開始想到了改 SAP 配置參數 dbs/db6/dbsl_substitute_literals 設置為 1 ,這樣SAP系統發送的全部SQL語句都是字面值..

可惜我沒找到相關的參數,網上的資料說這個參數是4.6版本的,新版本的找不到了,但是卻可以找到對應OracleMSSQL的參數.(再次暈倒..)

 

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;

經查詢,字面值已經傳入數據庫.這樣子,執行計划就是正確的了!


只要保證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、耗用時間(如下圖):
09年1月.bmp

通過 LOOP循環嵌套取091月份的數據耗時為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、耗用時間(如下圖):
改進后09年1月的數據.bmp

使用READ TABLE091月份的數據耗時為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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM