天天看點

隐式資料類型轉換案例一則

在OLTP系統中,要求對頻繁執行的SQL使用綁定變量(唯一值少的字段,如type、status等,資料分布不均是常見情況,這種字段不建議使用綁定變量)。

在使用綁定變量時,有時開發人員對使用的字段類型不是很清楚,比如銀行賬号、電話号碼、全數字型的序列号等,可以定義成number類型,也可以定義成varchar2類型。如果字段類型與綁定變量使用的資料類型不比對,就會發生隐式類型轉換。

常見的有害隐式類型轉換發生在下面情況:varchar2_col = :number_variable

即varchar2類型的字段,使用的綁定變量類型是number類型,這将造成字段上的索引不能使用,如果是頻繁執行的SQL,将會給系統帶來嚴重的性能問題。

下面是一個比較讓人迷惑的案例:

在對某客戶一個重要系統進行優化時,在AWR的TOP SQL中遇到了這個SQL:

隐式資料類型轉換案例一則

SQL代碼非常簡單:

SELECT NVL(MAX(DCC_PROC_ID),0)

FROM P_SERV A

WHERE ACC_NBR = :B2 AND SERV_ID <> :B1;

其中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的綁定變量等,也需要注意。