ZCOR0015的優化全過程記錄文檔
2015年3月,今天無意翻到這篇寫於2010年7月的文檔,回想那時的工作,畢業3年初出茅廬的我面對接觸不多的SAP+DB2竟敢操刀動斧,自信滿滿。 雖然這過程一路坎坷,數次判斷幾乎全都被打臉驗證,看着如小強般堅毅的我,哈哈~ 文檔里帶着情緒的措詞十分逗笑,可以洞悉那時的心情。 ——如今5年過去,卻久未聞硝煙,懷念ing~
內容說明:
紅色字體為突出個別文字的內容
黑色字體為普通過程闡述
藍色字體為圖解說明
黑色背景的字體為一些關鍵步驟點
優化目的:讓ZCOR0015能夠迅速取出數據,並且不通過MKPFMSEG中間表的處理.
業務的接受度: 跑1個月跨度的報表,要在5分鍾內返回結果,跑半年的報表,要在1小時內返回結果.
結果: ZCOR0015 統計一個月的數據20090101~20090131
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上的索引是非常不合理的,光是掃描一個索引就消耗那么多的資源,從這里就判定出索引的不合理性.
下面為這MSEG,MKPF這2張表在SDX里的狀況,行數,統計信息更新時間,頁數等等.與生產機的數據量基本同屬於一個數量級.
第二段是MKPF的兩個索引狀況,葉子數,索引字段的基數,統計信息時間.(還有levels 一般不超過3)
以下為這個執行計划消耗的I/O ,CPU,BUFFERS等等資源的狀況. (這里為估算值,並非真正的實際值)
接着開始在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';
速度不錯,兩次索引掃描,然后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 )'
看了下執行計划,終於找到問題了,
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的方式讓人十分不爽啊.
反倒是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;
爛的一塌糊塗,還是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開銷是十分小的,對比這個報表的消耗時間來說,基本可以忽略掉了)
如果把ABAP對SQL語句造成的影響降到最低,那么就是發送字面值了(字面值的意思就是類似 “MANDT=900”這樣的程序).
以下的公式是成立的:
SQL發送字面值+正確穩定的統計信息+合理的索引 ≈ 最優的執行計划 ≈ 最優的速度
這時候想辦法讓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;
經查詢,字面值已經傳入數據庫.這樣子,執行計划就是正確的了!
只要保證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、耗用時間(如下圖):
通過 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、耗用時間(如下圖):
使用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