一条高CPU语句的优化

来源:百度文库 编辑:神马文学网 时间:2024/04/28 11:21:04

一条高CPU语句的优化

                    作者 :OoNiceDream【转载时请务必以超链接形式标明文章原始出处和作者信息】
                    链接:http://www.dbaroad.me/archives/2008/12/high-cpu-sql.html

这两天抓取高CPU SQL的脚本频繁发出一个占用CPU较高的语句,语句如下:

SELECT T2.CONFLICT_ID,            T2.LAST_UPD,            T2.CREATED,            T2.LAST_UPD_BY,            T2.CREATED_BY,            T2.MODIFICATION_NUM,            T2.ROW_ID,            T1.X_ATTRIB_03,            T1.ROW_ID,            T1.MODIFICATION_NUM,            T1.CREATED_BY,            T1.LAST_UPD_BY,            T1.CREATED,            T1.LAST_UPD,            T1.CONFLICT_ID,            T1.PAR_ROW_ID            FROM USER.TABLE1 T1, USER.TABLE2 T2            WHERE T2.ROW_ID = T1.PAR_ROW_ID            AND (T2.TYPE = 'TFile' AND T1.X_ATTRIB_11 = 'Y')            AND (T2.PAR_ID = :1)            ORDER BY T2.PAR_ID, T2.NAME


执行计划如下:

[Execution Plan Information]                         -----------------------------------------------------------------------------------------------            | Operation                                 | PHV/Object Name         |  Rows | Bytes| Cost   |            -----------------------------------------------------------------------------------------------            |000[000]SELECT STATEMENT                   |---- 628327222.1 ----    |       |      |     13 |            |001[001]TABLE ACCESS BY INDEX ROWID        |TABLE1                   |     1 |   76 |      2 |            |002[002] NESTED LOOPS                      |                         |    10 |    1K|     13 |            |003[003]  TABLE ACCESS BY INDEX ROWID      |TABLE2                   |   491 |   46K|     12 |            |004[004]   INDEX FULL SCAN                 |TABLE2_F1                |    23K|      |    302 |            |005[003]  INDEX RANGE SCAN                 |TABLE1_U1                |     1 |      |      2 |            -----------------------------------------------------------------------------                         [Predicate Information]                         --------------------------------------------------------------------------------                         1 filter:"T1"."X_ATTRIB_11"='Y'            3 filter:"T2"."TYPE"='TFile' AND "T2"."PAR_ID"=:1            5 access:"T2"."ROW_ID"="T1"."PAR_ROW_ID"

乍看这个语句,问题也不是很大,主要的消耗是在索引TABLE2_F1上的INDEX FULL SCAN。
索引TABLE2_F1是建在表USER.TABLE2(T2表)的NAME列上。
其实也比较好理解,当有排序操作时,优化器比较偏向使用INDEX FULL SCAN,因为INDEX FULL SCAN返回的数据是有序的,这样就避免了排序

查看表USER.TABLE2(T2表),发现列PAR_ID是有索引的(TABLE2_M1)。
在PL/SQL工具中,查看下执行计划,发现走的是TABLE2_M1索引(INDEX RANGE SCAN),但多了一个排序的操作(SORT ORDER BY )

因为列PAR_ID采用的是绑定变量,决定试试代入不同的变量值,查看其执行计划是否有不同。
通过select PAR_ID,count(*) from USER.TABLE2 group by PAR_ID;
选取了两个有代表性的值,一个值较多,一个值较少。
在TOAD中运行语句,并通过加无效的HINT来区分(使SQL硬解析,避免bind peeking的影响),另开一个会话,抓取执行计划:

发现PAR_ID值较多的语句,走的是索引TABLE2_F1,执行计划如下:

HASH_VALUE VERSION_COUNT EXECUTIONS USERS_EXECUTING DISK_READS BUFFER_GETS ROWS_PROCESSED   CPU_TIME ELAPSED_TIME            ---------- ------------- ---------- --------------- ---------- ----------- -------------- ---------- ------------            4211970596             1          1               0          0       77718              0    4150000      4070344                         SQL_TEXT            -----------------------------------------------------------------            SELECT /*+ value1 */ T2.CONFLICT_ID,            T2.LAST_UPD,            T2.CREATED,            T2.LAST_UPD_BY,            T2.CREATED_BY,            T2.MODIFICATION_NUM,            T2.ROW_ID,            T1.X_ATTRIB_03,            T1.ROW_ID,            T1.MODIFICATION_NUM,            T1.CREATED_BY,            T1.LAST_UPD_BY,            T1.CREATED,            T1.LAST_UPD,            T1.CONFLICT_ID,            T1.PAR_ROW_ID            FROM USER.TABLE1 T1, USER.TABLE2 T2            WHERE T2.ROW_ID = T1.PAR_ROW_ID            AND (T2.TYPE = 'TFile' AND T1.X_ATTRIB_11 = 'Y')            AND (T2.PAR_ID = :1)            ORDER BY T2.PAR_ID, T2.NAME                         [Execution Plan Information]                         ----------------------------------------------------------------------------------------------            | Operation                                | PHV/Object Name         |  Rows | Bytes| Cost   |            ----------------------------------------------------------------------------------------------            |000[000]SELECT STATEMENT                  |---- 4211970596.0 ----   |       |      |   2134 |            |001[001]TABLE ACCESS BY INDEX ROWID       |TABLE1                   |     1 |   74 |      2 |            |002[002] NESTED LOOPS                     |                         |    42K|    7M|   2134 |            |003[003]  TABLE ACCESS BY INDEX ROWID     |TABLE2                   |    42K|    3M|    852 |            |004[004]   INDEX FULL SCAN                |TABLE2_F1                |     1M|      |  19872 |            |005[003]  INDEX RANGE SCAN                |TABLE1_U1                |     1 |      |      2 |            ----------------------------------------------------------------------------                         [Predicate Information]                         --------------------------------------------------------------------------------                         1 filter:"T1"."X_ATTRIB_11"='Y'            3 filter:"T2"."TYPE"='TFile' AND "T2"."PAR_ID"=:1            5 access:"T2"."ROW_ID"="T1"."PAR_ROW_ID"            sys@CMPR>

PAR_ID值较少的语句,走的是索引TABLE2_M1,执行计划如下:

HASH_VALUE VERSION_COUNT EXECUTIONS USERS_EXECUTING DISK_READS BUFFER_GETS ROWS_PROCESSED   CPU_TIME ELAPSED_TIME            ---------- ------------- ---------- --------------- ---------- ----------- -------------- ---------- ------------            245223696             1          1               0          2           6              0      10000        27223                         SQL_TEXT            ------------------------------------------------------------------            SELECT /*+ value2 */ T2.CONFLICT_ID,            T2.LAST_UPD,            T2.CREATED,            T2.LAST_UPD_BY,            T2.CREATED_BY,            T2.MODIFICATION_NUM,            T2.ROW_ID,            T1.X_ATTRIB_03,            T1.ROW_ID,            T1.MODIFICATION_NUM,            T1.CREATED_BY,            T1.LAST_UPD_BY,            T1.CREATED,            T1.LAST_UPD,            T1.CONFLICT_ID,            T1.PAR_ROW_ID            FROM USER.TABLE1 T1, USER.TABLE2 T2            WHERE T2.ROW_ID = T1.PAR_ROW_ID            AND (T2.TYPE = 'TFile' AND T1.X_ATTRIB_11 = 'Y')            AND (T2.PAR_ID = :1)            ORDER BY T2.PAR_ID, T2.NAME                         [Execution Plan Information]                         ----------------------------------------------------------------------------------------------            | Operation                                | PHV/Object Name         |  Rows | Bytes| Cost   |            ----------------------------------------------------------------------------------------------            |000[000]SELECT STATEMENT                  |---- 245223696.0 ----    |       |      |     53 |            |001[001]SORT ORDER BY                     |                         |   817 |  137K|     53 |            |002[002] TABLE ACCESS BY INDEX ROWID      |TABLE1                   |     1 |   74 |      2 |            |003[003]  NESTED LOOPS                    |                         |   817 |  137K|     27 |            |004[004]   TABLE ACCESS BY INDEX ROWID    |TABLE2                   |   817 |   78K|      3 |            |005[005]    INDEX RANGE SCAN              |TABLE2_M1                |  1429 |      |     12 |            |006[004]   INDEX RANGE SCAN               |TABLE1_U1                |     1 |      |      2 |            ----------------------------------------------------------------------------                         [Predicate Information]                         --------------------------------------------------------------------------------                         2 filter:"T1"."X_ATTRIB_11"='Y'            4 filter:"T2"."TYPE"='TFile'            5 access:"T2"."PAR_ID"=:1            6 access:"T2"."ROW_ID"="T1"."PAR_ROW_ID"

这样看来,是由于bind peeking的原因引起执行计划的不同。
考虑到SQL频繁占用CPU较高,尝试删除列PAR_ID的柱状图:

exec dbms_stats.set_column_stats('USER','TABLE2','PAR_ID',DISTCNT=>11674);

检查发现数据库发现,SQL已走索引TABLE2_M1,执行计划如下:

[Execution Plan Information]                         ----------------------------------------------------------------------------------------------            | Operation                                | PHV/Object Name         |  Rows | Bytes| Cost   |            ----------------------------------------------------------------------------------------------            |000[000]SELECT STATEMENT                  |---- 628327222.0 ----    |       |      |     11 |            |001[001]SORT ORDER BY                     |                         |    81 |   13K|     11 |            |002[002] TABLE ACCESS BY INDEX ROWID      |TABLE1                   |     1 |   74 |      2 |            |003[003]  NESTED LOOPS                    |                         |    81 |   13K|      4 |            |004[004]   TABLE ACCESS BY INDEX ROWID    |TABLE2                   |    81 |    7K|      2 |            |005[005]    INDEX RANGE SCAN              |TABLE2_M1                |   142 |      |      3 |            |006[004]   INDEX RANGE SCAN               |TABLE1_U1                |     1 |      |      2 |            ----------------------------------------------------------------------------                         [Predicate Information]                         --------------------------------------------------------------------------------                         2 filter:"T1"."X_ATTRIB_11"='Y'            4 filter:"T2"."TYPE"='TFile'            5 access:"T2"."PAR_ID"=:1            6 access:"T2"."ROW_ID"="T1"."PAR_ROW_ID"

经过一段时间的观察,该语句占用CPU高的现像消失,脚本未再抓到该高CPU语句。