1. 程式人生 > >優化案例1-盡量避免使用自定義函數進行大量運算

優化案例1-盡量避免使用自定義函數進行大量運算

遞歸 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-盡量避免使用自定義函數進行大量運算