记录一下 最近tuning的一例SQL
案例发生于report平台上的一个程序,以存储过程的形式存在,
透过每日对DB TIME的历史巡检,发现有异样,这张report把temp表空间都跑爆了都不会出结果,很是疑惑,
因为本身开发人员写的sql没有太大的诟病,不至于会把temp跑爆掉,并且已经经过测试投入生产使用了,所以并没太怀疑SQL的问题.
所以一开始并没有太在意sql的问题,- -并且这个时候看到的执行计划都是预估的,因为跑不出结果,直到报temp表空间不足的错误
比较纳闷,接着就开始一顿瞎倒腾,高水位,执行计划,10046,索引碎片,统计信息..
突然卡壳了,觉得这些都不是问题所在,然后开始单步调一调这个存储过程了,基本理清这个存储过程后,
去翻了翻AWR SQL Report..
咳咳,大跌眼镜,水落石出..
15:26:14 SYS PPTD 7.15 SQL> set autot off
15:26:18 SYS PPTD 7.15 SQL> Select *
15:26:19 2 from table(dbms_workload_repository.awr_sql_report_text(
15:26:19 3 1196179862,
15:26:19 4 1,
15:26:19 5 9444,
15:26:19 6 9447,
15:26:19 7 '7fgw1pgbc8hv7',
15:26:19 8 0));
Plan Statistics Snaps: 9444-9447 -> % Total DB Time is the Elapsed Time of the SQL statement divided into the Total Database Time multiplied by 100
Stat Name Statement Per Execution % Snap ---------------------------------------- ---------- -------------- ------- Elapsed Time (ms) 2,197,510 1,098,755.2 45.3 CPU Time (ms) 1,941,178 970,589.0 45.7 Executions 2 N/A N/A Buffer Gets 58,315 29,157.5 0.1 Disk Reads 726,046 363,023.0 79.1 Parse Calls 2 1.0 0.0 Rows ########## 21,049,910.0 N/A User I/O Wait Time (ms) 247,851 N/A N/A Cluster Wait Time (ms) 0 N/A N/A Application Wait Time (ms) 0 N/A N/A Concurrency Wait Time (ms) 3,859 N/A N/A Invalidations 0 N/A N/A Version Count 2 N/A N/A Sharable Mem(KB) 260 N/A N/A -------------------------------------------------------------
|
大体上这个存储过程的关键步骤切割后分为两步,
1,插入临时表.
2,然后根据临时表的标识数据,去匹配其他N张表的数据.
业务上的改变,导致部分join的条件并不是原来的逻辑了,原本1对1的column,现在是多对多,业务上的更改在report平台里没有及时的跟进,
导致笛卡尔集后翻了NNNNN倍,所以就会出现撑爆temp表空间的现象,找到问题点,沟通了下业务,更改之,OK
既来之,则安之,顺手把这部分的sql也调整了一下.
(为了美观,我把部分不重要的信息删去了)
一.tuning sql 1
Oracle Database
With the Partitioning, OLAP, Data Mining and Real Application Testing options
10:54:42 SYS PPTD 7.15 SQL> set autot traceonly
10:58:59 SYS PPTD 7.15 SQL> DELETE FROM ppt1d.R_TMP_AB008_A;
/******************************************删除并且插入数据至临时表***************************************/
已删除0行。
10:59:20 SYS PPTD 7.15 SQL> insert into ppt1d.R_TMP_AB008_A(ingotno)
10:59:20 2 select distinct brickno from ppt1d.squarer where type='S' AND creattime>=to_date('2010-5-17 00:00:00','YYYY-MM-DD HH24:MI:SS') AND
10:59:20 3 creattime<=to_date('2010-05-18 00:00:00','YYYY-MM-DD HH24:MI:SS');
已创建225行。
/******************************************开始查询******************************************************/
已用时间: 00: 00: 00.73
执行计划
----------------------------------------------------------
Plan hash value: 4168479773
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 225 | 95850 | 3040 (1)| 00:00:37 |
| 1 | SORT ORDER BY | | 225 | 95850 | 3040 (1)| 00:00:37 |
|* 2 | HASH JOIN RIGHT OUTER | | 225 | 95850 | 3039 (1)| 00:00:37 |
| 3 | VIEW | | 227 | 9080 | 452 (0)| 00:00:06 |
| 4 | NESTED LOOPS | | 227 | 10215 | 452 (0)| 00:00:06 |
| 5 | TABLE ACCESS FULL | R_TMP_AB008_A | 225 | 6075 | 2 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID | PRECLEAN | 1 | 18 | 2 (0)| 00:00:01 |
|* 7 | INDEX UNIQUE SCAN | SYS_C0028829 | 1 | | 1 (0)| 00:00:01 |
|* 8 | HASH JOIN RIGHT OUTER | | 225 | 86850 | 2586 (1)| 00:00:32 |
| 9 | VIEW | | 226 | 9266 | 452 (0)| 00:00:06 |
| 10 | NESTED LOOPS | | 226 | 9944 | 452 (0)| 00:00:06 |
| 11 | TABLE ACCESS FULL | R_TMP_AB008_A | 225 | 6075 | 2 (0)| 00:00:01 |
| 12 | TABLE ACCESS BY INDEX ROWID | GRINDING | 1 | 17 | 2 (0)| 00:00:01 |
|* 13 | INDEX UNIQUE SCAN | SYS_C0028800 | 1 | | 1 (0)| 00:00:01 |
|* 14 | HASH JOIN RIGHT OUTER | | 225 | 77625 | 2133 (1)| 00:00:26 |
| 15 | VIEW | | 225 | 11925 | 452 (0)| 00:00:06 |
| 16 | NESTED LOOPS | | 225 | 10800 | 452 (0)| 00:00:06 |
| 17 | TABLE ACCESS FULL | R_TMP_AB008_A | 225 | 6075 | 2 (0)| 00:00:01 |
| 18 | TABLE ACCESS BY INDEX ROWID | WIRESAW | 1 | 21 | 2 (0)| 00:00:01 |
|* 19 | INDEX UNIQUE SCAN | SYS_C0029033 | 1 | | 1 (0)| 00:00:01 |
|* 20 | HASH JOIN OUTER | | 225 | 65700 | 1681 (1)| 00:00:21 |
|* 21 | HASH JOIN RIGHT OUTER | | 225 | 33075 | 768 (1)| 00:00:10 |
| 22 | VIEW | | 225 | 12150 | 86 (2)| 00:00:02 |
|* 23 | HASH JOIN | | 225 | 10800 | 86 (2)| 00:00:02 |
| 24 | TABLE ACCESS FULL | R_TMP_AB008_A | 225 | 6075 | 2 (0)| 00:00:01 |
| 25 | TABLE ACCESS FULL | GMGK | 20809 | 426K| 84 (2)| 00:00:02 |
|* 26 | HASH JOIN | | 225 | 20925 | 681 (1)| 00:00:09 |
| 27 | TABLE ACCESS FULL | DEPTID_REPORT | 60 | 960 | 3 (0)| 00:00:01 |
| 28 | TABLE ACCESS BY INDEX ROWID | SQUARER | 1 | 50 | 3 (0)| 00:00:01 |
| 29 | NESTED LOOPS | | 225 | 17325 | 677 (0)| 00:00:09 |
| 30 | TABLE ACCESS FULL | R_TMP_AB008_A | 225 | 6075 | 2 (0)| 00:00:01 |
|* 31 | INDEX RANGE SCAN | SYS_C0028939 | 1 | | 2 (0)| 00:00:01 |
| 32 | VIEW | | 230 | 33350 | 912 (0)| 00:00:11 |
| 33 | NESTED LOOPS | | 230 | 15870 | 912 (0)| 00:00:11 |
| 34 | NESTED LOOPS | | 230 | 11960 | 452 (0)| 00:00:06 |
| 35 | TABLE ACCESS FULL | R_TMP_AB008_A | 225 | 6075 | 2 (0)| 00:00:01 |
| 36 | TABLE ACCESS BY INDEX ROWID| INSPECTING | 1 | 25 | 2 (0)| 00:00:01 |
|* 37 | INDEX UNIQUE SCAN | SYS_C0028809 | 1 | | 1 (0)| 00:00:01 |
| 38 | TABLE ACCESS BY INDEX ROWID | WIRESAW | 1 | 17 | 2 (0)| 00:00:01 |
|* 39 | INDEX UNIQUE SCAN | SYS_C0029033 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("S"."BRICKNO"="E"."BRICKNO"(+))
7 - access("TMP"."INGOTNO"="P"."BRICKNO")
8 - access("S"."BRICKNO"="C"."BRICKNO"(+))
13 - access("TMP"."INGOTNO"="G"."BRICKNO")
14 - access("S"."BRICKNO"="D"."BRICKNO"(+))
19 - access("TMP"."INGOTNO"="W"."BRICKNO")
20 - access("S"."BRICKNO"="F"."BRICKNO"(+))
21 - access("S"."BRICKNO"="B"."BRICKNO"(+))
23 - access("TMP"."INGOTNO"="G"."BRICKNO")
26 - access("M"."DEPTID"="S"."DEPTID")
31 - access("S"."BRICKNO"="TMP"."INGOTNO")
37 - access("I"."BRICKNO"="TMP"."INGOTNO")
39 - access("I"."BRICKNO"="W"."BRICKNO")
Note
-----
- dynamic sampling used for this statement
统计信息
----------------------------------------------------------
55 recursive calls
0 db block gets
4415 consistent gets
140 physical reads
0 redo size
21632 bytes sent via SQL*Net to client
3508 bytes received via SQL*Net from client
16 SQL*Net roundtrips to/from client
14 sorts (memory)
0 sorts (disk)
225 rows processed
驱动表200+条数据,4000+的逻辑读 ,小驱动表,nested loop大表,并且都有合理的索引, 看起来没什么大的问题,不过细看之下,
驱动表被用来”驱”的次数貌似有些不合常理,层次如下
(R_TMP_AB008_A INNER JOIN 业务表1
INNER JOIN 业务表2)
LEFT JOIN (R_TMP_AB008_A INNER JOIN 业务表3)
LEFT JOIN (R_TMP_AB008_A INNER JOIN 业务表4)
LEFT JOIN (R_TMP_AB008_A INNER JOIN 业务表5)
LEFT JOIN (R_TMP_AB008_A INNER JOIN 业务表6)
LEFT JOIN (R_TMP_AB008_A INNER JOIN 业务表7
INNER JOIN 业务表8)
看得我很纠结,基本上是按照业务”常规理解”的模式直接写出来的sql,细想一下,还是可以将其优化的.
如果驱动表R_TMP_AB008_A不大的时候,大家都走nested loop,只要有合理的索引,问题就不大.
但是随着驱动表的数据量增大(几千~上万的时候),nested loop效率会逐渐降低,cost比不上hash join时
,CBO选择HASH JOIN,这时Tuning1_A的逻辑读大概会比调整后的Tuning1_B多10~20个百分点
以下为调整后的JOIN关系
(R_TMP_AB008_A INNER JOIN 业务表1
INNER JOIN 业务表2
LEFT JOIN 业务表3
LEFT JOIN 业务表4
LEFT JOIN 业务表5
LEFT JOIN 业务表6
LEFT JOIN 业务表7)
逻辑读由4415降低至3707
这样的情况其实并不少见,即是”由业务逻辑直译成SQL”,开发人员在写SQL的时候,多留个心眼,就不会存在这样的问题了.
总结一下:
对业务的部分变更,需要及时的嗅到,update之.
PS:我在WORD里编辑好了,排序都是很整齐的,贴到windows live writer里后,就歪掉了 是字体的问题??...哪位大侠有好的方法啊?求指点.
OVER.
执行计划
----------------------------------------------------------
Plan hash value: 3277539272
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 233 | 45435 | 2575 (1)| 00:00:31 |
| 1 | SORT ORDER BY | | 233 | 45435 | 2575 (1)| 00:00:31 |
| 2 | NESTED LOOPS OUTER | | 233 | 45435 | 2574 (1)| 00:00:31 |
| 3 | NESTED LOOPS OUTER | | 228 | 38760 | 2118 (1)| 00:00:26 |
| 4 | NESTED LOOPS OUTER | | 226 | 34352 | 1666 (1)| 00:00:20 |
|* 5 | HASH JOIN | | 225 | 30375 | 1215 (1)| 00:00:15 |
| 6 | TABLE ACCESS FULL | DEPTID_REPORT | 60 | 960 | 3 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID | SQUARER | 1 | 50 | 3 (0)| 00:00:01 |
| 8 | NESTED LOOPS | | 225 | 26775 | 1212 (1)| 00:00:15 |
| 9 | NESTED LOOPS OUTER | | 225 | 15525 | 536 (1)| 00:00:07 |
|* 10 | HASH JOIN OUTER | | 225 | 10800 | 86 (2)| 00:00:02 |
| 11 | TABLE ACCESS FULL | R_TMP_AB008_A | 225 | 6075 | 2 (0)| 00:00:01 |
| 12 | TABLE ACCESS FULL | GMGK | 20809 | 426K| 84 (2)| 00:00:02 |
| 13 | TABLE ACCESS BY INDEX ROWID| WIRESAW | 1 | 21 | 2 (0)| 00:00:01 |
|* 14 | INDEX UNIQUE SCAN | SYS_C0029033 | 1 | | 1 (0)| 00:00:01 |
|* 15 | INDEX RANGE SCAN | SYS_C0028939 | 1 | | 2 (0)| 00:00:01 |
| 16 | TABLE ACCESS BY INDEX ROWID | GRINDING | 1 | 17 | 2 (0)| 00:00:01 |
|* 17 | INDEX UNIQUE SCAN | SYS_C0028800 | 1 | | 1 (0)| 00:00:01 |
| 18 | TABLE ACCESS BY INDEX ROWID | PRECLEAN | 1 | 18 | 2 (0)| 00:00:01 |
|* 19 | INDEX UNIQUE SCAN | SYS_C0028829 | 1 | | 1 (0)| 00:00:01 |
| 20 | TABLE ACCESS BY INDEX ROWID | INSPECTING | 1 | 25 | 2 (0)| 00:00:01 |
|* 21 | INDEX UNIQUE SCAN | SYS_C0028809 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("M"."DEPTID"="S"."DEPTID")
10 - access("TMP"."INGOTNO"="G"."BRICKNO"(+))
14 - access("TMP"."INGOTNO"="W"."BRICKNO"(+))
15 - access("S"."BRICKNO"="TMP"."INGOTNO")
17 - access("TMP"."INGOTNO"="GR"."BRICKNO"(+))
19 - access("TMP"."INGOTNO"="P"."BRICKNO"(+))
21 - access("TMP"."INGOTNO"="I"."BRICKNO"(+))
Note
-----
- dynamic sampling used for this statement
统计信息
----------------------------------------------------------
4 recursive calls
0 db block gets
3707 consistent gets
0 physical reads
0 redo size
21632 bytes sent via SQL*Net to client
2347 bytes received via SQL*Net from client
16 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
225 rows processed