上周四下午的时候收到Oracle DB的High Disk I/O Loading Session警示邮件,查找发现是如下这段SQL导致High Disk I/O:
SELECT * FROM CLW3014 WHERE AUDIT_DATE = (SELECT MAX(AUDIT_DATE) AUDIT_DATE FROM CLW3014 WHERE BOOK_NO = :B3 AND COLUMN_NAME = :B2 AND ENTRY_OFFICE = :B1 AND AUDIT_TYPE = 'A') AND AUDIT_TYPE ='A' AND COLUMN_NAME = :B2 AND ENTRY_OFFICE = :B1
这里特意做个FLAG记录一下。
SQL Tuning后性能大幅度提升:
SQL运行时间:17s 下降到 0.4s
Cost开销:18976 下降到 1
Consistent gets为:86807 下降到 13
Physical reads为:86785 下降到 9
SQL> set autotrace traceonly
SQL> ......sql......
Elapsed: 00:00:16.77
Execution Plan
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name |Rows|Bytes|Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 178 | 18976 (1) | 00:03:48|
|* 1 | TABLE ACCESS FULL | CLW3014 | 1 | 178 | 18975 (1) | 00:03:48|
| 2 | SORT AGGREGATE | | 1 | 39 | | |
|* 3 | TABLE ACCESS BY INDEX ROWID | CLW3014 | 1 | 39 | 1 (0) | 00:00:01|
|* 4 | INDEX RANGE SCAN | CLW3014_IDX_3| 10 | | 1 (0) | 00:00:01|
------------------------------------------------------------------------------------------------------------
Statistics
-----------------------------------------------------------------
1 recursive calls
0 db block gets
86807 consistent gets
86785 physical reads
128 redo size
1441 bytes sent via SQL*Net to client
365 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
3 rows processed
通过查看执行计划,该子查询SQL通过 BOOK_NO = :B3 走的是Index(CLW3014_IDX_3)扫描查询,即:
SELECT MAX(AUDIT_DATE) AUDIT_DATE FROM CLW3014 WHERE BOOK_NO = :B3 AND COLUMN_NAME = :B2 AND ENTRY_OFFICE = :B1 AND AUDIT_TYPE = 'A'
主SQL没有走Index扫面查询,全表扫描查询CLW3014(FULL TABLE SCAN CLW3014),性能参数:
运行时间为:17s
Cost为:18976
Consistent gets为:86807
Physical reads为:86785
看到有以AUDIT_TYPE和SOURCE_RECORD_SEQ两个栏位建立组合Index:
CLW3014_IDX_1 on CLW3014 (AUDIT_TYPE, SOURCE_RECORD_SEQ)
看到主SQL有where条件:AUDIT_TYPE ='A' ,为何不走Index(CLW3014_IDX_1)扫描查询呢,因为该TABLE的AUDIT_TYPE栏位值纵总共才三个:
所以对于AUDIT_TYPE栏位来说,重复性太高,缺少SOURCE_RECORD_SEQ栏位作为条件的话走Index(CLW3014_IDX_1)扫描性能会更差,Oracle自动选择了最优的FULL TABLE SCAN查询的方式。
查看数据发现AUDIT_DATE栏位的值可重复性不高,如果以AUDIT_DATE栏位创建Index,则性能大大提升:
SQL> create index CLW3014_IDX_5 on CLW3014 (AUDIT_DATE) tablespace CLWIDX pctfree 10;
Index created.
SQL> set autotrace traceonly
SQL> ......sql......
Elapsed: 00:00:00.40
Execution Plan
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name |Rows |Bytes|Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 178| 1 (0) | 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID | CLW3014 | 1 | 178| 1 (0) | 00:00:01 |
|* 2 | INDEX RANGE SCAN | CLW3014_IDX_5 | 5 | | 1 (0) | 00:00:01 |
| 3 | SORT AGGREGATE | | 1 | 39| | |
|* 4 | TABLE ACCESS BY INDEX ROWID| CLW3014 | 1 | 39| 1 (0) | 00:00:01 |
|* 5 | INDEX RANGE SCAN | CLW3014_IDX_3 | 10 | | 1 (0) | 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
13 consistent gets
9 physical reads
0 redo size
1457 bytes sent via SQL*Net to client
365 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
3 rows processed
看到主子SQL都走Index扫面查询,性能参数:
运行时间为:0.4s
Cost为:1
Consistent gets为:13
Physical reads为:9