hint不當索引,影像多表連線方式,最終導致SQL執行緩慢
需求:一個SQL執行特別慢,無法返回結果,需要進行優化,最終返回結果即可。
一、SQL分析
二、嘗試執行,觀測執行計劃
三、修改SQL
四、問題總結
一、SQL分析
1)SQL文字,執行時間,執行使用者 使用者brjljk sql執行時間,2935分鐘 sql_text select c.hphm, c.ccdjrq, c.clpp1, c.clxh, c.zt, c.syr, c.wfsj, c.wfxw, c.dsr, c.xxly, c.syq, c.wfsj1, d.wfnr, e.dlmc, c.xxly1, c.dsr1from (select /*+ index(b idx_violation_wfsj)*/ a.hphm, a.ccdjrq, a.clpp1, a.clxh, a.zt, a.syr, a.wfsj, a.wfxw, a.dsr, a.xxly, a.syq, b.wfsj wfsj1, b.wfxw wfxw1, b.wfdd wfdd1, b.xxly xxly1, b.dsr dsr1from sjs20181022 a right join trff_app.vio_violation b on a.hphm = b.hphm where a.wfsj <> b.wfsj and (b.wfsj < add_months(a.wfsj, 12) and b.wfsj > add_months(a.wfsj, -12)) and a.wfsj > to_date('2018-08-01', 'yyyy-mm-dd') and a.wfsj< to_date('2018-09-01', 'yyyy-mm-dd') order by a.hphm, a.wfsj, b.wfsj) c, trff_app.vio_codewfdm d, trff_app.frm_roaditem e where c.wfxw1 = d.wfxw and c.wfdd1 = e.dldm
2)查詢會話等待事件
SQL> select sid,serial#,event,sql_id,status,(sysdate-logon_time)*86400 as "s",
last_call_et,username,inst_id,MACHINE from gv$session where status='ACTIVE' and username is not null;
SID SERIAL# EVENT SQL_ID STATUS s LAST_CALL_ET USERNAME INST_ID MACHINE
---------- ---------- ------------------------------ ------------- -------- ---------- -------
1776 28345 db file sequential read 2vcdzpaknk46s ACTIVE 180100 176352 BRJLJK 1 xx
3)查詢sql文字
SQL> select sql_text from v$sqlarea where sql_id ='&a';
SQL> select sql_text from v$sqltext where sql_id ='2vcdzpaknk46s';
SQL> select sql_text from v$sqlstats where sql_id ='2vcdzpaknk46s';
4)查詢執行計劃
select * from table(dbms_xplan.display_cursor('&sql',null,'PEEKED_BINDS'));
SQL> select * from table(dbms_xplan.display_awr('2vcdzpaknk46s',null));
------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 129M(100)| |
| 1 | SORT ORDER BY | | 309 | 56238 | 129M (1)|432:58:14 |
| 2 | HASH JOIN | | 309 | 56238 | 129M (1)|432:58:14 |
| 3 | HASH JOIN | | 309 | 50985 | 129M (1)|432:58:13 |
| 4 | HASH JOIN | | 309 | 37389 | 129M (1)|432:58:13 |
| 5 | TABLE ACCESS FULL | SJS20181022 | 10455 | 847K| 891 (2)| 00:00:11 |
| 6 | TABLE ACCESS BY INDEX ROWID| VIO_VIOLATION | 144M| 5226M| 129M (1)|432:57:51 |
| 7 | INDEX FULL SCAN | IDX_VIOLATION_WFSJ | 144M| | 583K (1)| 01:56:47 |
| 8 | TABLE ACCESS FULL | VIO_CODEWFDM | 1069 | 47036 | 13 (0)| 00:00:01 |
| 9 | TABLE ACCESS FULL | FRM_ROADITEM | 5212 | 88604 | 22 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------
5)查詢SQL等待事件
select count(*),event,count(distinct session_id) from gv$active_session_history
where sql_id='2vcdzpaknk46s' group by event;
COUNT(*) EVENT COUNT(DISTINCTSESSION_ID)
---------- ------------------------------ -------------------------
27652 db file sequential read 1
22 gc cr block 2-way 1
258 gc cr disk read 1
293 1
6)查詢執行計劃最慢的步驟(failed)
select count(*),sql_plan_line_id
from gv$active_session_history
where sql_id='2vcdzpaknk46s'
group by sql_plan_line_id
order by 2;
--生產環境10.2.0.5,11g才有的欄位
7)表碎片
表碎片會導致全表掃描更消耗資源,本次慢不是由於全表掃描的問題
8)資料量
通過dba_tables,num_rows,dba_segments,bytes查詢得到資訊如下
a表 hash join 驅動表,30萬條記錄
B表 hash join 被驅動表,1億條記錄,表100G大小
二、嘗試執行,觀測執行計劃
1)確認優化重點四個表中,從執行計劃看,重點為 | 6 | TABLE ACCESS BY INDEX ROWID| VIO_VIOLATION | 144M| 5226M| 129M (1)|432:57:51 | | 7 | INDEX FULL SCAN | IDX_VIOLATION_WFSJ | 144M| | 583K (1)| 01:56:47 | 該SQL是a +b 的集合,轉換為c最後與其它表進行關聯查詢 2)對a+b表的查詢進行優化及測試 思路A,是否由於時間取值範圍導致的問題 and (b.wfsj < add_months(a.wfsj, 12) and b.wfsj > add_months(a.wfsj, -12)) and a.wfsj > to_date('2018-08-01', 'yyyy-mm-dd') and a.wfsj < to_date('2018-09-01', 'yyyy-mm-dd') explain plan for select a.hphm,a.wfsj,b.wfsj from BRJLJK.sjs20181022 a right join trff_app.vio_violation b on a.hphm=b.hphm where b.wfsj > to_date('2017-01-01','yyyy-mm-dd') and b.wfsj <to_date('2019-01-01','yyyy-mm-dd') and a.wfsj>to_date('2018-08-01','yyyy-mm-dd') and a.wfsj<to_date('2018-09-01','yyyy-mm-dd') order by a.hphm,a.wfsj, b.wfsj; 1* select * from table(dbms_xplan.display()) PLAN_TABLE_OUTPUT ---------------------------------------------------------------------- Plan hash value: 1015943026 ----------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 18958 | 629K| 115K (1)| 00:23:12 | | 1 | SORT ORDER BY | | 18958 | 629K| 115K (1)| 00:23:12 | |* 2 | TABLE ACCESS BY INDEX ROWID| VIO_VIOLATION | 2 | 34 | 13 (0)| 00:00:01 | | 3 | NESTED LOOPS | | 18958 | 629K| 115K (1)| 00:23:12 | |* 4 | INDEX FAST FULL SCAN | SJS20181022_IND_HPHM | 10455 | 173K| 277 (2)| 00:00:04 | |* 5 | INDEX RANGE SCAN | IDX_VIOLATION_HPHM | 12 | | 3 (0)| 00:00:01 | ----------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("B"."WFSJ">TO_DATE(' 2017-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "B"."WFSJ"<TO_DATE(' 2019-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss')) 4 - filter("A"."WFSJ">TO_DATE(' 2018-08-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "A"."WFSJ"<TO_DATE(' 2018-09-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss')) 5 - access("A"."HPHM"="B"."HPHM") select * from table(dbms_xplan.display_cursor('004nmwuabm1qr',null,'PEEKED_BINDS')) PLAN_TABLE_OUTPUT ----------------------------------------------------------------------------------------------- SQL_ID 004nmwuabm1qr, child number 0 ------------------------------------- select a.hphm, a.ccdjrq, a.clpp1, a.clxh, a.zt, a.syr, a.wfsj, a.wfxw, a.dsr, a.xxly, a.syq, b.wfsj wfsj1, b.wfxw wfxw1, b.wfdd wfdd1, b.xxly xxly1, b.dsr dsr1 from BRJLJK.sjs20181022 a right join trff_app.vio_violation b on a.hphm=b.hphm where (b.wfsj < add_months(a.wfsj, 12) and b.wfsj >add_months(a.wfsj, -12)) and a.wfsj>to_date('2018-08-01','yyyy-mm-dd') and a.wfsj<to_date('2018-09-01','yyyy-mm-dd') order by a.hphm,a.wfsj, b.wfsj Plan hash value: 3321285990 PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 136K(100)| | | 1 | SORT ORDER BY | | 309 | 37389 | 136K (1)| 00:27:23 | |* 2 | TABLE ACCESS BY INDEX ROWID| VIO_VIOLATION | 1 | 38 | 13 (0)| 00:00:01 | | 3 | NESTED LOOPS | | 309 | 37389 | 136K (1)| 00:27:23 | |* 4 | TABLE ACCESS FULL | SJS20181022 | 10455 | 847K| 891 (2)| 00:00:11 | |* 5 | INDEX RANGE SCAN | IDX_VIOLATION_HPHM | 12 | | 3 (0)| 00:00:01 | --------------------------------------------------------------------------------- PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter(("B"."WFSJ"<ADD_MONTHS(INTERNAL_FUNCTION("A"."WFSJ"),12) AND "B"."WFSJ">ADD_MONTHS(INTERNAL_FUNCTION("A"."WFSJ"),-12))) 4 - filter(("A"."WFSJ">TO_DATE(' 2018-08-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "A"."WFSJ"<TO_DATE(' 2018-09-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))) 5 - access("A"."HPHM"="B"."HPHM") 32 rows selected. 結論A的考慮是錯誤的,add_months並不會導致執行計劃消耗更多的資源 思路B:多表連線的問題? 讓sql從hash join 轉換為nest loop試試,本次sql 取消hint即可, 為了不增加伺服器負擔, 使用explain plan for 方式 SQL> explain plan for select a.hphm,a.wfsj,b.wfsj from BRJLJK.sjs20181022 a right join trff_app.vio_violation b on a.hphm=b.hphm where (b.wfsj < add_months(a.wfsj, 12) and b.wfsj >add_months(a.wfsj, -12)) and a.wfsj>to_date('2018-08-01','yyyy-mm-dd') and a.wfsj<to_date('2018-09-01','yyyy-mm-dd') order by a.hphm,a.wfsj, b.wfsj; Explained. SQL> select * from table(dbms_xplan.display()); PLAN_TABLE_OUTPUT --------------------------------------------------------------------------- Plan hash value: 1015943026 --------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 309 | 10506 | 115K (1)| 00:23:12 | | 1 | SORT ORDER BY | | 309 | 10506 | 115K (1)| 00:23:12 | |* 2 | TABLE ACCESS BY INDEX ROWID| VIO_VIOLATION | 1 | 17 | 13 (0)| 00:00:01 | | 3 | NESTED LOOPS | | 309 | 10506 | 115K (1)| 00:23:12 | |* 4 | INDEX FAST FULL SCAN | SJS20181022_IND_HPHM | 10455 | 173K| 277 (2)| 00:00:04 | |* 5 | INDEX RANGE SCAN | IDX_VIOLATION_HPHM | 12 | | 3 (0)| 00:00:01 | --------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("B"."WFSJ"<ADD_MONTHS(INTERNAL_FUNCTION("A"."WFSJ"),12) AND "B"."WFSJ">ADD_MONTHS(INTERNAL_FUNCTION("A"."WFSJ"),-12)) 4 - filter("A"."WFSJ">TO_DATE(' 2018-08-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "A"."WFSJ"<TO_DATE(' 2018-09-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss')) 5 - access("A"."HPHM"="B"."HPHM") 21 rows selected. 發現nest loop 方式挺快的,繼續測試(上述測試只測試3個欄位) 使用原SQLa+b的兩個表SQL不做變動,執行測試,發現執行計劃未改變; 使用需要優化的SQL文字,刪除hint,進行explain plan for 進行測試,執行計劃未改變
三、修改SQL
刪除Hint,讓SQL走nest loop 方式,10s內返回結果 SQL> select c.hphm, c.ccdjrq, c.clpp1, c.clxh, c.zt, c.syr, c.wfsj, c.wfxw, c.dsr, c.xxly, c.syq, c.wfsj1, d.wfnr, e.dlmc, c.xxly1, c.dsr1 from (select a.hphm, a.ccdjrq, a.clpp1, a.clxh, a.zt, a.syr, a.wfsj, a.wfxw, a.dsr, a.xxly, a.syq, b.wfsj wfsj1, b.wfxw wfxw1, b.wfdd wfdd1, b.xxly xxly1, b.dsr dsr1 from BRJLJK.sjs20181022 a right join trff_app.vio_violation b on a.hphm = b.hphm where a.wfsj <> b.wfsj and (b.wfsj < add_months(a.wfsj, 12) and b.wfsj > add_months(a.wfsj, -12)) and a.wfsj > to_date('2018-08-01', 'yyyy-mm-dd') and a.wfsj < to_date('2018-09-01', 'yyyy-mm-dd') order by a.hphm, a.wfsj, b.wfsj) c, trff_app.vio_codewfdm d, trff_app.frm_roaditem e where c.wfxw1 = d.wfxw and c.wfdd1 = e.dldm; 52519 rows selected. Elapsed: 00:00:05.08 Execution Plan ---------------------------------------------------------- Plan hash value: 2181500870 ----------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 309 | 56238 | 136K (1)| 00:27:24 | | 1 | SORT ORDER BY | | 309 | 56238 | 136K (1)| 00:27:24 | |* 2 | HASH JOIN | | 309 | 56238 | 136K (1)| 00:27:24 | |* 3 | HASH JOIN | | 309 | 50985 | 136K (1)| 00:27:23 | |* 4 | TABLE ACCESS BY INDEX ROWID| VIO_VIOLATION | 1 | 38 | 13 (0)| 00:00:01 | | 5 | NESTED LOOPS | | 309 | 37389 | 136K (1)| 00:27:23 | |* 6 | TABLE ACCESS FULL | SJS20181022 | 10455 | 847K| 891 (2)| 00:00:11 | |* 7 | INDEX RANGE SCAN | IDX_VIOLATION_HPHM | 12 | | 3 (0)| 00:00:01 | | 8 | TABLE ACCESS FULL | VIO_CODEWFDM | 1069 | 47036 | 13 (0)| 00:00:01 | | 9 | TABLE ACCESS FULL | FRM_ROADITEM | 5212 | 88604 | 22 (0)| 00:00:01 | ----------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("B"."WFDD"="E"."DLDM") 3 - access("B"."WFXW"="D"."WFXW") 4 - filter("A"."WFSJ"<>"B"."WFSJ" AND "B"."WFSJ"<ADD_MONTHS(INTERNAL_FUNCTION("A"."WFSJ"), 12) AND "B"."WFSJ">ADD_MONTHS(INTERNAL_FUNCTION("A"."WFSJ"),-12)) 6 - filter("A"."WFSJ">TO_DATE(' 2018-08-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "A"."WFSJ"<TO_DATE(' 2018-09-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss')) 7 - access("A"."HPHM"="B"."HPHM") Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 211168 consistent gets 108 physical reads 116 redo size 3555229 bytes sent via SQL*Net to client 39003 bytes received via SQL*Net from client 3503 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 52519 rows processed
四、問題總結
1)使用Nest loop方式,被驅動表及時迴圈查詢30萬次,比想象中的快很多很多 2)本次sql未優化前走hash join方式的原因是,hint 索引是時間列, 而Nest loop方式需要驅動表的查詢結果輸出身份證,被驅動表拿著身份證, 去被驅動表中索取記錄;驅動表在本次執行計劃無變化,被驅動表從時間欄位索引,轉換走 IDX_VIOLATION_HPHM, 也就是說,由於索引的選擇度的問題,Oracle認為 hash join的連線方式 優於 date索引(hint)找到對應的rowid,然後找到hphm欄位值 3)今後,在使用hint前,通過測試,選擇合適的hint