在OLTP系統中,要求對頻繁執行的SQL使用綁定變量(惟一值少的字段,如type、status等,數據分佈不均是常見狀況,這種字段不建議使用綁定變量)。sql
在使用綁定變量時,有時開發人員對使用的字段類型不是很清楚,好比銀行帳號、電話號碼、全數字型的序列號等,能夠定義成number類型,也能夠定義成varchar2類型。若是字段類型與綁定變量使用的數據類型不匹配,就會發生隱式類型轉換。性能優化
常見的有害隱式類型轉換發生在下面狀況:varchar2_col = :number_variable微信
即varchar2類型的字段,使用的綁定變量類型是number類型,這將形成字段上的索引不能使用,若是是頻繁執行的SQL,將會給系統帶來嚴重的性能問題。oracle
下面是一個比較讓人迷惑的案例:函數
在對某客戶一個重要系統進行優化時,在AWR的TOP SQL中遇到了這個SQL:工具
SQL代碼很是簡單:性能
SELECT NVL(MAX(DCC_PROC_ID),0)優化
FROM P_SERV Aspa
WHERE ACC_NBR = :B2 AND SERV_ID <> :B1;.net
其中ACC_NBR(varchar2類型)和SERV_ID都是選擇性很好的字段,兩個字段上都有索引,由於SERV_ID是不等於條件,不能使用索引,那麼這個SQL正常狀況應該是使用ACC_NBR字段上的索引纔是最佳路徑。並且平均執行時間不該該超過1毫秒,實際顯示平均執行時間達到了540毫秒,確定有問題。
經過sqlhc工具收集了該SQL相關信息,發現SQL的執行計劃有3個:
其中1最好,2和3都與1差了幾百倍的性能。而根據常理,只有執行計劃1纔是比較正常的性能表現。
再看SQL執行狀況,1和2兩個計劃同時存在,效率差異很是明顯:
再來分別看3個執行計劃的具體內容:
執行計劃1,plan_hash_value=1228755719,使用了ACC_NBR字段上的索引,沒有問題。
執行計劃2,plan_hash_value=164228054,使用全表掃描的執行計劃,在執行計劃顯示的最後部分,Predicate Information裏,有TO_NUMBER(「ACC_NBR」)=:B2 字樣,再結合上面部分的Peeked Binds信息,ACC_NBR字段使用的綁定變量是NUMBER類型,由此能夠判斷,這是由於綁定變量使用的數據類型與字段自己的類型不匹配,優化器自動對字段作了隱式類型轉換,至關於在字段上使用了TO_NUMBER函數,這種狀況就不能使用ACC_NBR字段上的索引:
再來看第3個執行計劃,plan_hash_value=1669185283,這個執行計劃由於是歷史執行計劃,看不到Predicate Information信息(只有sql執行計劃存在於V$SQL_PLAN視圖時,纔會有Predicate Information信息,DBA_HIST_SQL_PLAN不保存這個信息),可是綁定變量使用的數據類型可以看到,是NUMBER類型。這種狀況下,優化器仍是由於隱式類型轉換不能使用ACC_NBR字段上的索引,轉而使用兩個索引全掃描再作index join的執行計劃,效率更差。
通過開發人員對代碼的核對,發現這段SQL在兩個不一樣的代碼段中被調用,一段代碼使用了正確的綁定變量類型(varchar2),而另外一段則使用了number類型的綁定變量,這就形成了同一個sql_id, 同時存在多個不一樣執行計劃的狀況。這種狀況應該屬於應用代碼不規範。
研發人員將使用number類型的綁定變量更換成varchar2類型後,SQL就都使用執行計劃1了,執行效率大幅提升,CPU使用率也降低不少。
有的DBA在遇到這種多個執行計劃同時存在的狀況(這個狀況比較特殊),可能會考慮使用SQL profile來固定執行計劃,可是固定的執行計劃只對正確使用綁定變量類型的SQL生效,對於不正確綁定變量類型,SQL profile也不起做用。
總結:
研發人員在使用綁定變量時,應該密切關注表字段定義的數據類型,尤爲是當電話號碼、帳號信息等全數字的字段定義成varchar2時,千萬不要使用number類型的綁定變量,不然會嚴重消耗系統資源,SQL自己性能也會很是差。
另外一種狀況是number數據類型的字段,在綁定變量是varchar2時,也會發生隱式類型轉換。可是,這個隱式類型轉換是發生在綁定變量上,所以不會對執行計劃有影響,是無害的。
如何檢查和發現系統中存在的隱式類型轉換? 這裏簡單列出最多見的一種檢查方法:
select sql_id,object_owner,operation,options,object_name,
filter_predicates,projection
from v$sql_plan where filter_predicates like 'TO_NUMBER%'
此外還有一些其餘的隱式類型轉換,如date類型的字段,使用了timestamp的綁定變量等,也須要注意。
關注「老虎劉談SQL優化」,分享老虎劉那些年的SQL優化案例!
腳本分享在QQ羣:16778072
歡迎轉發分享給更多的朋友
爲了方便交流,有興趣的朋友能夠加入同名微信羣:
本文分享自微信公衆號 - 老虎劉談oracle性能優化(sql_tigerliu)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。