今天這個案例相對比較簡單,算是對基本原理的一個增強理解吧。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源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。