新建的索引解決了問題,結論真的對嗎?

    今天這個案例相對比較簡單,算是對基本原理的一個增強理解吧。sql


    客戶提供了一份其餘公司作的優化報告,其中有個SQL確實是完成了優化,優化的結果也不錯,可是給出的優化理由倒是不許確的。性能優化


下面是報告對該SQL進行分析優化的描述:微信

SQL代碼以下:oracle

SELECT COUNT(1)oop

  FROM CB_PROBLEM A性能

  LEFT OUTER JOIN CB_PROJECT B ON A.PRJ_SEQ = B.PRJ_SEQ測試

  LEFT OUTER JOIN BS_LINE C ON A.PRB_LINE = C.LINE_SEQ優化

  LEFT OUTER JOIN BP_FLOW D ON A.PRB_FLOW = D.FLOW_SEQspa

  LEFT OUTER JOIN BP_NODE E ON A.PRB_NODE = E.NODE_SEQ.net

  LEFT OUTER JOIN BS_EFFECT F ON A.EFFECT_LVL = F.VALUE_SEQ

  LEFT OUTER JOIN BS_FREQUENCY G ON A.FREQUENCY_LVL = G.VALUE_SEQ

  LEFT OUTER JOIN BS_S EVERITY H ON A.SEVERITY_LVL = H.VALUE_SEQ

 WHERE A.STATUS != 0

AND A.PRB_ORG IN

(

SELECT ORG_ID

FROM SP_ORG

START WITH ORG_ID IN (:1, ......, :18)

CONNECT BY PRIOR ORG_ID = SUP_ORG

)

   AND A.PRJ_SEQ IN

(

SELECT PRJ_SEQ

FROM CB_PROJECT

WHERE 1 = 1 AND PRJ_NAME LIKE '%xxxxxxx%'

);


執行計劃以下(紅字是老虎劉補註):


(老虎劉注:看到這個執行計劃有人可能會問,SQL語句中那麼多作left join的表,上面爲何一個都沒看到?

    這是由於最後要的結果是count(1),left join的表若是關聯字段都是惟一的(這個是根據執行計劃判斷出來的),優化器會很聰明的把這些表從執行計劃中消除,結果是等價的)。


    其中主表CB_PROBLEM大小爲312M,約44萬行數據。SQL平均執行時間爲6分鐘


CB_PROBLEM表當前索引狀況:

INDEX_OWNER  INDEX_NAME  COLUMN_NAME    COLUMN_POSITION 

------------ --------------------------- -------------- --------------- 

ICIMS01      IDX_CB_PROBLEM_PRJ_SEQ      PRJ_SEQ                    1 

ICIMS01      IDX_PROBLEM_ID                          PRB_ID                       1 

ICIMS01      IDX_PROBLEM_SEQ                      PRB_SEQ                   1 

ICIMS01      IDX_PROBLEM_SEQ                      PRJ_SEQ                    2 

ICIMS01      SYS_C006663                                 PRB_SEQ                    1

(老虎劉注: IDX_PROBLEM_SEQ索引首字段與SYS_C006663重複,SYS_C006663索引能夠刪掉

SQL中涉及的字段的選擇性:                                      

COLUMN_NAME     NUM_DISTINCT

------------------------ ------------

PRB_ORG                         10319

PRJ_SEQ                         75264


原報告由此得出的結論是:

    能夠看出,SQL中出現的字段PRJ_SEQ 已經在多個索引中出現,並且其選擇性也不錯, 爲75264,之因此沒走上該字段的索引,是因爲該字段對應的子查詢返回結果過多,達到1萬多行,因此優化器沒有其上的索引。


    SQL中還涉及另外一個字段PRB_ORG,它的選擇性也不錯,爲10319,而且其上沒有索引,經分析,建議在PRB_ORG和PRJ_SEQ上建立複合索引。

create index idx_test on ICIMS01.cb_problem(prb_org,prj_seq) online ;  


咱們先來看看增長索引後的SQL執行狀況:

執行時間只須要2.73秒,比原來的6分鐘有很是大的提升。執行計劃以下:


下面咱們來分析一下爲何說這個索引建立的理由是錯誤的。


    執行計劃中的步驟10由原來的全表掃描變成了索引掃描,這一步是建立索引的功勞。


    執行計劃中,SP_ORG表做爲初始驅動表,cb_problem表是做爲nested loops的被驅動表,與驅動表的關聯字段只有一個:PRB_ORG,雖然使用的是兩字段聯合索引,但實際上只用到了第一個字段:PRB_ORG


    接下來cb_problem表又作爲nested loops的驅動表,經過PRJ_SEQ字段驅動CB_PROJECT表時,使用的是CB_PROJECT表的關聯字段PRJ_SEQ字段上的索引,與此時的驅動表cb_problem表PRJ_SEQ字段上是否有索引無關。


    也就是說,原結論建立兩個字段上的索引,其實只須要一個字段就夠了,增長一個字段也不會提升索引在這個SQL中的選擇性(有可能在其餘SQL同時使用兩個字段作謂詞條件時是高效的)。

       

總結:

    在哪一個表上建立索引,建立怎樣的索引才能使SQL執行效率最高,須要完全搞清楚SQL執行計劃。有時問題解決了,可能仍是沒有理解真正的緣由。建立索引不是靠猜想,而是通過仔細分析後得出的結果。


    兩表關聯作nested loops時,驅動表要求結果集(通過謂詞條件過濾後)要小,表的謂詞條件字段上通常要存在索引(不是關聯字段);被驅動表的關聯字段上要存在索引,這是基本常識。


好比下面這種OLTP系統常見的兩表關聯SQL:

select .... from t1,t2

where t1.object_id = t2.object_id and t1.object_name='T1';

    執行計劃應該是t1表作驅動表,使用object_name字段上的索引,選出少許記錄,t2表作被驅動表,object_id字段(關聯字段)上要存在索引。

    就這個sql自己而言,t1表的object_id字段上有沒有索引是沒有關係的。在某個客戶現場發現這樣的SQL,建立的是t1表object_id和object_name兩個字段的聯合索引,那就大錯特錯了。

    

後話:

    優化後的執行計劃,根據顯示的估值行數,驅動表的行數仍是比較高,前兩個表究竟是作nested loops 仍是作hash join好一些還很差說(沒有測試過),若是是hash join更好一些,那麼上面那個索引其實也沒有建立的必要了。


分享持續更新中,敬請關注:老虎劉談SQL優化


腳本分享在QQ羣:16778072    

歡迎轉發分享給更多的朋友

爲了方便交流,有興趣的朋友能夠加入同名微信羣:



本文分享自微信公衆號 - 老虎劉談oracle性能優化(sql_tigerliu)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索