天天看點

SQL稽核:OR展開與子查詢優化案例詳解

SQL稽核:OR展開與子查詢優化案例詳解

黃廷忠(網名:認真就輸)

雲和恩墨技術專家

個人部落格:http://www.htz.pw/

sql性能問題診斷

下面來看看一條 sql,主查詢使用 or 與子查詢聯合一起使用,導緻子查詢不能展開,隻能走 filter,子查詢被輪詢很多次,消耗大量邏輯讀。

SQL稽核:OR展開與子查詢優化案例詳解

這裡需要注意紅色框部分,可以看到括号中存在主查詢列過濾,并且在後面跟一條子查詢做or運算。

下面來檢視此 sql 的執行計劃:

SQL稽核:OR展開與子查詢優化案例詳解

執行計劃中,可以看到在謂詞資訊部分有多個 filter,在執行計劃中有3個 filter,但是在 sql*plus 中,隻有兩個 or,是以需要弄清楚哪些 filter 是過濾,哪些 filter 是子查詢沒有展開導緻的。

兩個紅色框的部分就是子查詢沒有展開導緻的,箭頭部分隻是用于列過濾的。這裡有一個判斷準則:

當 filter 下面有兩個兒子表(結果集)的時候,此時filter就是子查詢沒有展開導緻,此時的 filter 可以看成是執行完子查詢後的過濾; 當 filter 下面隻有一個兒子表(結果集),此時的 filter 是做行過濾的;

filter 的原理跟 nl 的原理類型,當驅動表換回一行時,被驅動表執行一次。但是 filter 還與 nl 有2點不同的是:

當驅動表傳回有重複值時,被驅動不會執行 當被驅動表找到比對的行時,立即終止本次循環

在 sql 中見到出現 filter 時,不能直接說性能不好。那到底怎麼判斷有 filter 時,sql 是的性能是否好呢?其實覺得可以根據主查詢傳回行數很少時,使用 filter 性能可能很好,主查詢傳回的行數很多時,走 filter 性能肯定不好。 

基礎資訊分析

下面來看看 v$sql 中的統計資訊:

SQL稽核:OR展開與子查詢優化案例詳解

每個字段的值在上面案列中已經提過,這裡就不再說明了。

可以看到平均傳回一行,消耗的邏輯讀是2076324:

SQL稽核:OR展開與子查詢優化案例詳解

通過 sql 每次執行傳回的邏輯讀與 sql 關聯的表占用的大小,可以間接的推斷此 sql 由于不停的輪詢消耗大量的邏輯讀,可以知道主表傳回的行數肯定很多的(這裡還有一種特殊情況就是消耗 temp 的情況)。

下面檢視一下主表傳回的行數:

SQL稽核:OR展開與子查詢優化案例詳解

這裡隻簡單的查詢表 group by 的值,這裡根據上面的值估計主表傳回的結果集很多,如果要準确的值,可以關聯上面2張表查詢。

sql 改寫

現在知道原因了,那麼這個 sql 優化基本完成70%的工作了,現在就是想辦法來怎麼處理了:讓 sql 不走 filter。

由于原來 sql 就使用 distinct ,是以這裡不需要考慮重複值的情況。根據本 sql 的特征,将 sql 修改成如下的:

SQL稽核:OR展開與子查詢優化案例詳解

這裡将 or 修改成 union 的方式。

sql性能優化效果

修改後的 sql 的執行計劃:

SQL稽核:OR展開與子查詢優化案例詳解

從執行計劃中,我們已經沒有看到 filter 資訊了。

下面檢視 sql 執行的統計資訊

SQL稽核:OR展開與子查詢優化案例詳解

這裡看到 sql 執行的邏輯都從原來的2,076,324降到現在的11了。效果很明顯。

總結

本條 sql 優化是通過改寫 sql 來完成的,意味着業務需要修改 sql,可能會出現業務修改完 sql 再上線,這中間可能會消耗大量的時間,并且如果 sql 後期出現性能問題,需要再次修改 sql 的成本也會更高。建議在優化的 sql 時候,可以通過提示+sql profile 來固定執行,達到不修改 sql 的前提下,優化 sql。這樣業務可以不需要修改代碼,并且後期随時可以通過修改 sql profile 中的提示資訊來修改 sql 的執行計劃。

良好的應用性能,必須依賴高度優化的sql性能,我們推薦使用者通過sql稽核在更前的時段發現和修正問題,進而防患于未然。

<b></b>