閱讀360 返回首頁    go 阿裏雲 go 技術社區[雲棲]


【雲和恩墨大講堂】Oracle線上嘉年華第二講

編輯手記:Oracle線上嘉年華,正在持續分享中。本次的主題是係統割接中的SQL解析問題和結合業務的SQL優化改寫技巧。

1嘉賓介紹

640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy

小魚(鄧秋爽)

雲和恩墨專家,有超過5年超大型數據庫專業服務經驗,擅長oracle 數據庫優化、SQL優化和troubleshooting


新係統割接的library cache問題

這是我們在做係統割接的時候的一個案例,可能並不是很常見,這個案例是將Oracle 11g升級到12c的時候遇到的問題,出現了大量的library cache的問題。具體情況是:

新係統割接後,不定時出現大量library cache lock、library cache:mutex X,幾分鍾後係統自動恢複。

在短暫的時間裏,我們來不及做systemdump的,而且出現的頻率和時間也是不固定的,很難抓取到當時係統的信息。

先獲取故障時段的AWR報告:

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=我們看到這兩個等待事件已經占了整體DB time的72%,大家看到這個問題,可能一般都會想到解析,


我們從以下幾個角度分析:

  • TopSQL的解析、執行頻率是否合理:

故障點的library lock相關的SQL來看,基本占據db time、parse閾值高的Top SQL解析、執行頻率並沒有數量級的增加,他們更像是受害者。

  • Oraclebug是否存在:

係統版本Oracle 12.1.0.2,經過原廠排查並不存在相關bug引起。

  • SQL解析是否存在問題,綁定變量使用分析:

查看awr報告硬解析次數很高,但是挖掘sharedpool發現係統中並未發現大幅度未使用綁定變量的SQL。

 

在oracle 10g的時候,V$SQLAREA視圖有一個FORCE_MATCHING_SIGNATURE 參數,可以將SQL經過綁定變量代替後生成一個hashvalue值,通過這個值找到未使用綁定變量的SQL,而開發商的SQL的質量比較高,並未發現核心業務SQL未使用綁定變量的情況。

這樣看來,這個問題是很棘手的,硬解析次數很高,但我們找不到對應的SQL在哪裏。

 

我們接著分析,來看AWR報告裏麵的time model statistic

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

我們看到紅色標記的部分,解析時間消耗了63.74%、解析失敗消耗了50.55%。

解析失敗是什麼?Oracle的解釋是這樣的:

failed parse:語法、權限等無法執行的SQL解析,也是硬解析,並且解析失敗是不能被重用的,當然它也不會存儲在V$SQLAREA視圖中,所以也挖掘不到這類SQL


我們如何去發現在係統中解析失敗的SQL呢?

Oracle提供了event 10035,會將解析失敗的SQL記錄到alert 日誌裏麵

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

從上麵的日誌可以看到各種解析錯誤的代碼,其中error=942,表示:表不存在,因此判斷這是他們做係統變更的時候做過一些表的刪除,我們可能在係統割接的時候都會做一些舊表的drop或者rename,這時候一定要嚴格挖掘應用端的代碼,將下線的業務代碼停掉,避免錯誤解析導致數據庫出現嚴重的性能問題。


SQL優化改寫技巧

接下來和大家分享執行計劃結合業務邏輯的一個等價改寫的例子

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

案例中的SQL如上,大致由兩部分組成,上下各是一個標量子查詢,然後用union all聯合在一起做了一個order by,在結果顯示中使用了分頁。

我們通過腳本獲得該SQL單次邏輯讀將近18000000.返回行數為10行,響應時間達到104036MS。


這是個很複雜的SQL,包含標量子查詢、表連接、unionall、排序、分頁,還有一些複雜的decode、nvl等函數,通過awr報告我們得知該SQL單次執行需要1500多萬到1900多萬的邏輯讀,平均都隻返回10行數據,單次執行時間也要100秒左右。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

我們可以將SQL簡化如下:

select * from (select row_.*, rownum

rownum_ from

(SELECT ST.* FROM (Select …TO_CHAR(T.SIGNDATE,

‘YYYYMMDDHH24:MI:SS’) AS SIGNDATE …(標量子查

詢1)、(標量子查詢2)... From

MM_MK_CUSTMGR_SIGN T WHERE T.REGION = 14

Union all

Select …TO_CHAR(T.SIGNDATE,

'YYYYMMDDHH24:MI:SS') AS SIGNDATE …(標量子查詢

1)、(標量子查詢2)... From MM_MK_CUSTMGR_SIGN T

WHERE T.REGION = 14

AND T.SIGNOUTOID IS NOT NULL) ST

ORDER BY SIGNDATE DESC) row_ where rownum <= :1)

where rownum_ > :2

 

對於這種複雜的SQL,我們先看執行計劃

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

這個執行計劃我們做過相應刪減。

在優化SQL中,我們優先考慮能否優化cost高的步驟,比如大表全表掃描、大表全索引快速掃描、跳躍索引掃描、大表排序等cost消耗;

其次看filter(優化了的nestedloop)、nested loop、hash join、笛卡爾積等表關聯步驟cost消耗。


在上麵的標量子查詢中,Cost消耗最高的在這個view操作,COST消耗達到了14M、rows達到了501K,而這個view是由兩部分union all組成的。

在下麵的標量子查詢中,兩部分union all發現上層部分主查詢MM_MK_CUSTMGR_SIGN T估算返回501k Rows,下層主查詢則隻有1Rows數據。

注:在Oracle的估算中是不存在0 Rows的情況,如果評估的結果是0,會算作1.


對於標量子查詢,我們簡單做個介紹,就是說優化器在這種情況下永遠隻做一種操作就是filter,這是一種變相優化nest loop。對於這種標量自從查詢,我們知道其實SQL之所以出現問題是因為下麵的501k導致需要驅動上麵那堆複雜的標量子查詢,


那麼如何優化呢?

常規優化:對於標量子查詢,可以使用等價改寫為表的外連接方式讓其走hash jion的執行計劃,但是如果標量子查詢中有大表則並不合適,該SQL恰恰包含大表,並不適合用常規的等價外連接的方式來改寫。

業務結合執行計劃分析:那麼這個ORDER BYSIGNDATE DESC排序後rownum能否推進到主查詢MM_MK_CUSTMGR_SIGN T表中,rownum限製後再去驅動標量子查詢,減少標量子查詢的循環次數。

 

接下來主要針對第二種,結合業務進行分析改寫。

在上麵的SQL中,是先取501k數據做了驅動,然後再做標量子查詢和order by的操作,我們能不能把order by的操作推回到標量子查詢前麵,這樣子的話標量子查詢要驅動的隻是前麵排序取rownum限製條件的數據,我們通過畫圖的方式來分析一下:

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

首先是兩個同樣的表,做了標量子查詢的操作,這裏的數據是501k,然後標量子查詢完了之後,做了order by後rownum的限製,這是原SQL的執行業務邏輯。

 

我認為應該寫成這樣,我們想限製標量子查詢的循環次數,那我們就先去對主查詢取order by排序rownum限製後的數據,再將主查詢取出來的這部分數據去驅動標量子查詢,做完後再做一次order by rownum的限製。(這裏並不會改變SQL的業務邏輯,雖然我們是先排序取rownum限製了,但是標量子查詢時主查詢是先排序還是後排序取rownum限製對於主查詢返回結果集沒有任何影響)

 

根據這種思路,我把SQL改寫如下:

SELECT * FROM

(SELECT row_.*, rownum rownum_ FROM

(SELECT ST.* FROM

(SELECT …TO_CHAR(T.SIGNDATE,

‘YYYYMMDDHH24:MI:SS’) AS SIGNDATE …(標量子查詢1)、(標

量子查詢2)... FROM

(SELECT * FROM tbcs.MM_MK_CUSTMGR_SIGN x

WHERE x.REGION = 14

ORDER BY SIGNDATE DESC offset :2 rows

FETCH NEXT :1 rows only) T

UNION ALL

SELECT …TO_CHAR(T.SIGNDATE,

'YYYYMMDDHH24:MI:SS') AS SIGNDATE …(標量子查詢1)、(標

量子查詢2)... FROM

(SELECT * FROM tbcs.MM_MK_CUSTMGR_SIGN x

WHERE x.REGION = 14

ORDER BY SIGNDATE DESC offset :2 rows

FETCH NEXT :1 rows only ) T 

) ST ORDER BY SIGNDATE DESC) row_

WHERE rownum <= :1)

WHERE rownum_ > :2

 

其中紅色部分是12C的改寫方式。是用一個分析函數的方式去做的。

它的執行計劃如下:

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

先訪問表MM_MK_CUSTMGR_SIGN排序取rownum限製(前10行數據後),再去驅動那堆複雜的標量子查詢,最後再次排序取rownum條件數據,邏輯讀從千萬級降低到了26661。

 

這個SQL在改寫後,資源消耗降低了許多,基本上能夠滿足業務的需求。

如果我們再去剖析原SQL代碼,發現union all部分是同一個MM_MK_CUSTMGR_SIGN表的查詢,下麵那個UNION ALL部分查詢出來的結果是上麵UNION ALL部分的子集。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

而跟研發溝通發現實際上union all的下層查詢可以去掉,去掉後則該SQL無需改寫rownum就可以直接推進到主查詢中,從這個例子可以看到不嚴謹的代碼容易造成性能隱患,影響優化器評估最合理的執行計劃。

 

通過以上分享,我們得出:

1、線上係統變更、表下線需要嚴格挖掘應用端代碼,避免因為表結構變更導致SQL解析錯誤。

2、複雜業務邏輯對應的SQL需要核查,對於不需要的結果和表關聯等盡可能去掉,簡化表關聯數量,合理利用優化器。

 


文章轉自數據和雲公眾號,原文鏈接

最後更新:2017-07-18 20:36:29

  上一篇:go  【雲和恩墨大講堂】複合索引與綁定變量
  下一篇:go  【雲和恩墨大講堂】SQL玩轉AWR裸數據