優化案例1-盡量避免使用自定義函數進行大量運算
阿新 • • 發佈:2017-07-28
遞歸 sed clas -s bytes per pla ont har
案例說明
在月底進行代碼優化檢查過程中。在SQL檢查過程之執行次數最多的SQL。發現SQL_ID為grk7dk5amf5m7和gzzzkzbfg8j2m 在半個小時內產生大約分別15億次執行。邏輯讀也有15G
其實SQL本身很簡單;是一個自定義的分割函數。
原SQL
select to_char(a.logintime, ‘yyyymmdd‘), to_char(a.logintime, ‘HH24‘), 2552, substr(a.qn, 5, 4) ad, regexp_substr(a.qn,‘[^_]+‘, 1, 2) channelid, a.account, sysdate from ssdk_acc_login a where to_char(a.logintime, ‘yyyymmdd‘) = ‘20170728‘ and splitstr(a.qn, 2, ‘_‘) in (select cid from tbl_channel where channel_tag like ‘廣點通%‘);
該SQL的執行計劃
Execution Plan ----------------------------------------------------------Plan hash value: 2543353618 ----------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -----------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 7236 | 558K| 2445 (1)| 00:00:30 | | 1 | VIEW | VM_NWVW_2 | 7236 | 558K| 2445 (1)| 00:00:30 | | 2 | HASH UNIQUE | | 7236 | 607K| 2445 (1)| 00:00:30 | |* 3 | HASH JOIN | | 7236 | 607K| 2444 (1)| 00:00:30 | |* 4 | TABLE ACCESS FULL | TBL_CHANNEL | 154 | 2772 | 5 (0)| 00:00:01 | | 5 | TABLE ACCESS BY INDEX ROWID| SSDK_ACC_LOGIN | 95232 | 6324K| 2438 (1)| 00:00:30 | |* 6 | INDEX RANGE SCAN | IND_SDK_LOGIN_TIME | 95232 | | 670 (1)| 00:00:09 | ----------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("CID"="SPLITSTR"(INTERNAL_FUNCTION("A"."QN"),2,‘_‘)) 4 - filter("CHANNEL_TAG" LIKE ‘廣點通%‘) 6 - access(TO_CHAR(INTERNAL_FUNCTION("LOGINTIME"),‘yyyymmdd‘)=‘20170728‘) Statistics ---------------------------------------------------------- 152770 recursive calls 1833240 db block gets 307000 consistent gets 0 physical reads 0 redo size 1465885 bytes sent via SQL*Net to client 20225 bytes received via SQL*Net from client 1793 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 26867 rows processed
從執行計劃上來看。存在有152770遞歸調用和約200M的邏輯讀。但是返回的行數只有2萬條。邏輯讀跟返回的行數嚴重失調。本身表ssdk_acc_login是一個大表。
對有經驗的人來說;產生大量的遞歸條用;很容易看出問題跟自定義函數SPLITSTR有關。
通過等價改下SQL
select to_char(a.logintime, ‘yyyymmdd‘), to_char(a.logintime, ‘HH24‘), 2552, substr(a.qn, 5, 4) ad, regexp_substr(a.qn, ‘[^_]+‘, 1, 2) channelid, a.account, sysdate from ssdk_acc_login a where to_char(a.logintime, ‘yyyymmdd‘) = ‘20170728‘ and regexp_substr(a.qn,‘[^_]+‘,1,2) in (select cid from tbl_channel where channel_tag like ‘廣點通%‘);
該SQL 的執行計劃
Execution Plan ---------------------------------------------------------- Plan hash value: 2829062945 --------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 47 | 3478 | 2443 (1)| 00:00:30 | | 1 | NESTED LOOPS | | 47 | 3478 | 2443 (1)| 00:00:30 | | 2 | SORT UNIQUE | | 154 | 2772 | 5 (0)| 00:00:01 | |* 3 | TABLE ACCESS FULL | TBL_CHANNEL | 154 | 2772 | 5 (0)| 00:00:01 | |* 4 | TABLE ACCESS BY INDEX ROWID| SSDK_ACC_LOGIN | 47 | 2632 | 2437 (1)| 00:00:30 | |* 5 | INDEX RANGE SCAN | IND_SDK_LOGIN_TIME | 95232 | | 669 (1)| 00:00:09 | --------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - filter("CHANNEL_TAG" LIKE ‘廣點通%‘) 4 - filter("CID"= REGEXP_SUBSTR ("A"."QN",‘[^_]+‘,1,2)) 5 - access(TO_CHAR(INTERNAL_FUNCTION("LOGINTIME"),‘yyyymmdd‘)=‘20170728‘) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 214489 consistent gets 0 physical reads 556 redo size 1345632 bytes sent via SQL*Net to client 20324 bytes received via SQL*Net from client 1802 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 27004 rows processed
執行計劃
與之前的SQL的執行計劃相比;代價相差無幾。連接方式由hash連接改為NESTED LOOPS
統計信息:
遞歸調用:前面是152770次。現在是0次。
邏輯讀: 前面是約200M。現在是約20M。相差10倍。
redo日誌。前面不產生。現在產生556kb。影響不大。
排序: 前面不產生;現在產生排序。此服務器專用於oracle。內存48G。影響不大。
綜上所述:用正則函數regexp_substr來替換 自定義函數splitstr更好。盡量避免使用自定義函數進行大量運算
優化案例1-盡量避免使用自定義函數進行大量運算