現在項目中經常 出現有的時候 SQL跑的很快, 有時候確像 蝸牛。。
我當時覺得奇怪, 爲啥 哥優化好了一個SQL, 怎麼 又慢的?? 於是哥開始反思, 這個哥的第一反應是 統計信息導致的問題。 統計信息 把rows 算的錯誤的太離譜了。
導致本來走hash 的走了 NL。
於是哥找出SQl, 看了下執行計劃, 果然
/*
1 Plan hash value: 2350308821
2
3 -----------------------------------------------------------------------------------------------------
4 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
5 -----------------------------------------------------------------------------------------------------
6 | 0 | SELECT STATEMENT | | 1 | 105 | 7 (0)| 00:00:01 |
7 | 1 | SORT AGGREGATE | | 1 | 105 | | |
8 | 2 | NESTED LOOPS OUTER | | 1 | 105 | 7 (0)| 00:00:01 |
9 | 3 | NESTED LOOPS | | 1 | 84 | 4 (0)| 00:00:01 |
10 | 4 | NESTED LOOPS OUTER | | 1 | 77 | 3 (0)| 00:00:01 |
11 | 5 | TABLE ACCESS FULL | T_PUB_BILL_ACTION_3 | 1 | 67 | 2 (0)| 00:00:01 |
12 |* 6 | INDEX RANGE SCAN | DIM_TASK_SPEC_IDX | 1 | 10 | 1 (0)| 00:00:01 |
13 |* 7 | INDEX RANGE SCAN | IDX_OPENBILL_3 | 1 | 7 | 1 (0)| 00:00:01 |
14 |* 8 | TABLE ACCESS BY INDEX ROWID| DIM_STAFF | 1 | 21 | 3 (0)| 00:00:01 |
15 |* 9 | INDEX RANGE SCAN | IDX_DIM_STAFF | 2 | | 1 (0)| 00:00:01 |
16 -----------------------------------------------------------------------------------------------------
一看 果然錯誤的太離譜了, CBO 算成1了, 這裏哥 想到了, 用hint 固定執行計劃, 發現這個固然可以, 但是 哥想到爲啥 算成1了。
哥看了一下 統計信息 收集的時間, 一看發現 好像是在 9點17分鐘的樣子收集的, 哥明白了。
這個正是由於truncate table 導致的, 哥查了V$SQL , 發現這樣搞的還蠻頻繁的, truncate 之後, 正好 oracle 來收集統計信息了, 於是哥又收集了一下統計信息
於是哥 看了一下執行計劃, 果然變了 , 變成hash 了。
/*
1 Plan hash value: 1978552090
2
3 ------------------------------------------------------------------------------------------------------
4 | Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
5 ------------------------------------------------------------------------------------------------------
6 | 0 | SELECT STATEMENT | | 1047K| 163M| | 24493 (1)| 00:04:54 |
7 |* 1 | HASH JOIN RIGHT OUTER | | 1047K| 163M| | 24493 (1)| 00:04:54 |
8 |* 2 | TABLE ACCESS FULL | DIM_TASK_SPEC | 51 | 1479 | | 3 (0)| 00:00:01 |
9 |* 3 | HASH JOIN RIGHT OUTER| | 1047K| 134M| 4544K| 24483 (1)| 00:04:54 |
10 |* 4 | TABLE ACCESS FULL | DIM_STAFF | 81538 | 3583K| | 231 (1)| 00:00:03 |
11 |* 5 | HASH JOIN | | 1047K| 89M| 4504K| 18966 (1)| 00:03:48 |
12 | 6 | TABLE ACCESS FULL | T_OPEN_BILLINFO_3 | 100K| 3323K| | 4625 (1)| 00:00:56 |
13 | 7 | TABLE ACCESS FULL | T_PUB_BILL_ACTION_3 | 1047K| 55M| | 10741 (1)| 00:02:09 |
14 ------------------------------------------------------------------------------------------------------
15
16 Predicate Information (identified by operation id):
17 ---------------------------------------------------
18
19 1 - access("D"."TASK_SPEC_ID"(+)="A"."ACTION_TYPE")
20 2 - filter("D"."SYS_ID"(+)=4)
21 3 - access("C"."ORG_ID"(+)="A"."OPERATOR_GROUP" AND "C"."STAFF_ID"(+)="A"."OPERATOR")
22 4 - filter("C"."SYS_ID"(+)=4)
23 5 - access("A"."BILL_ID"="B"."BILL_ID")*/
這個由於經常要 truncate 。 於是 哥需要 固定執行計劃, 用hint 固定也可以, 但開發人員好像不太會搞 hint, 那哥只好第二招,讓
開發人員方便開發, 固定統計信息。
於是哥, DBMS_STATS.lock_table_stats , 那就以下腳本
DBMS_STATS.GATHER_TABLE_STATS(OWNNAME => 'JS_ZWXN', TABNAME => 'T_PUB_BILL_ACTION_3', ESTIMATE_PERCENT => 100, METHOD_OPT => 'for all columns size repeat', DEGREE => 8, GRANULARITY => 'ALL',CASCADE => TRUE);
DBMS_STATS.lock_table_stats(OWNNAME => 'JS_ZWXN', TABNAME => 'T_PUB_BILL_ACTION_3');
對了優化前後 效率提高 20多倍吧, 如果兩個表在數據量 最多時候算, 能提升100多倍。
所以 優化來說, 肯定保證 統計信息 正確, 起碼不能錯誤的太離譜了。