實戰SQL優化之340行從7分15s到24s的優化歷程

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

        花費了我將近一天半的工做時間,在幾近放棄的狀況下完成的一個逆襲,我也算是拼了一把。
sql

環境:阿里雲 ECS 11.2.0.1 ADG Oracleide

環境分析

前幾天優化了一條SQL搞定之後,覺得晚上日終批量報表沒有問題了,而後昨天想着檢查一下,看awr報告,截取晚上1點到2點的時間段,赫然映入眼簾。SQL換了,四條徹底相似的SQL語句,有300多行,着實嚇了一條,和開發瞭解到,這四條SQL其實差很少算是一條SQL,只是最後取模的的值分爲1,2,3,4,因此運行了4次,但是每一次都將近400s,函數

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

而後就手動的執行了其中的一條語句,取得是模爲2的爲例進行執行以下:測試

(因爲語句有340行,太長,這裏截取片斷)
優化

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

語句大概相似這樣的一個結構。阿里雲

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

分析來看,總共要獲得37000條左右的數據,邏輯讀1.7億,物理讀220w,3次內存排序,耗時7分15s;3d

從執行計劃上看,SQL語句屢次回表,而且其中還有一些超過400w的表,而且都是全表掃描。而後咱們來分析一下SQL語句和執行計劃,從執行計劃看是否是以爲此起彼伏挺有規律的,而後看看眼瞎的那條300多行的SQL語句,7個標量子查詢和t1到t6的6張結果集表,都有很相同的部分,7個標量子查詢查的數據都是count(b.activationtime),只不過是由於條件不一樣,所獲得的值不一樣而已,t1 - t6 的6張結果集表的語句一樣查詢的結果集字段都是同樣的,只不過也是由於條件的不一樣而不一樣罷了。code

那麼到這裏,能夠很明確的表示,這個SQL寫的無比的爛,等於無緣無故的多掃了屢次的大表,而且,進行了冗餘的hash鏈接,致使資源消耗巨大。blog

其中總結出來的,不一樣的條件就是:排序

trunc(b.activationtime, 'dd') = c.BUSINESSDATE

trunc(b.completetime, 'dd') = c.BUSINESSDATE

trunc(b.completetime, 'dd') is null

b.tasktypeflag = '00'

trunc(b.activationtime, 'dd') <= c.BUSINESSDATE

標量子查詢中和t7關聯的條件都是相同的:

a.processid = t7.processid

b.operatorcode = t7.operatorcode

b.taskname = t7.taskname

而t1到t6的6張表來講,都相似以下:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

一樣也是由於如上所寫的不一樣條件的組合致使的結果集不一樣,進而被簡單羅列成了多張表。

而後致使,最後出現了幾十條外連接的條件。相似以下形式:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

因此改寫勢在必行,也是惟一的方式。

第一次嘗試的描述

因此就以前的分析思想,進行了第一次改寫,可是雖然有了減小回表,減小冗餘錶鏈接的思想以及集合的思想在內,可是並無實質性的改變,只是簡單的將數據t1到t6表的集合放到了一塊兒,然而標量子查詢由於沒法從一個集合衍生出如原標量查詢出的結果集,致使沒法修改標量子查詢,因此修改後的結果收效甚微,修改以下:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

這裏簡單介紹就是將t7表,已經t1-t6表的公共部分拿出來寫成了with as語句,而後將外聯接的查詢所有拿了出來作呈了with as查詢,進行的改寫形式。

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

咱們看到時間從7分15s降到了5分39s,邏輯讀沒有什麼變化依然1.7億,物理讀25w降低近10倍,內存排序從3次到1次,要說有效果也是有的,可是依然很慢啊。而後就再思考緣由,其實這裏的第一次修改以後好處是,將t1-t6這6張表,進行了一次合併排序,而後再根據條件的不一樣再組合出這6張表,這就減小了一些回表的資源消耗,可是標量那裏的資源消耗依然沒有減小,由於標量查詢壓根就沒有改過。

之因此沒有進行修改,以前也提到了,按照我這種集合思路來看,修改以後,好像是能夠進行集合一次再根據條件的不一樣組合進行獲得不同的值,可是事實證實獲得的結果對應列所在行的數據所有都同樣,也就是說這種方式是不行的。

第二次修改的嘗試

而後我就開始考慮第二種思路:

大體思路以下:

而後咱們將標量的查詢拿出來和t1-t6這6張表的語句拿出來看的話,發現,t1-t6的子查詢當中包含有標量子查詢的一些部分表和條件。而標量子查詢中部分條件是和t7表作的關聯,而t1,t3,t4,t6四張表其實和合爲一張表,而後把不一樣的條件所獲得的count(b.activationtime)值,都放到同一個子查詢當中,做爲列,相似t2,t5也是同樣的效果,而後就能夠把6次回表變成2次。

再來看標量子查詢,其實仍是同樣針對trunc(b.activationtime, 'dd') <= c.BUSINESSDATE條件的不動,仍是標量子查詢,由於它和前邊的6個標量子查詢不太同樣。而對於這6個標量子查詢來講,仍是按順序1,3,4,6相似,2,5相似,也能夠這樣進行關聯,一樣也是爲了將屢次回表減小。

這裏採用的技術就是,針對count來講,能夠在子查詢中使用decode,case when語句,來根據條件設置默認值,我這裏設置條件爲真爲1,條件爲假爲0,而後sum集合,其獲得的效果和count是一致的。

修改語句以下:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

大體解釋一下,就是將標量子查詢中的公共部分以及t1-t6表公共部分拿出來作的一個with as,而後利用decode和case when函數作的合併,寫成sql

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

查看執行計劃,時間從5分39秒到了1分44s,標量子查詢的此起彼伏的規律消失,邏輯讀從1.7億降到4000w,可是物理讀變成了36w,內存排序從1次變爲0次。

結果來看改進不小,講道理,查4w條數據,不到2分鐘,還能夠了。可是仍是發現,這裏還有一個標量子查詢,若是也一樣像前邊的標量子查詢提取出來呢,會是什麼效果呢,原覺得確實應該變化不會很大的,可是仍是想嘗試一下。

第三次改寫嘗試

將trunc(b.activationtime, 'dd') <= c.BUSINESSDATE條件的標量子查詢單獨提取出來,而後用t7再關聯了一張表,外連接也一樣關聯出相關的外連接條件,語句以下:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

此次是在第二次改寫的基礎上進行的修改,修改部分如上。

從340行愣是減小到了200行。

執行計劃:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

從執行計劃看,1分44s到24s,邏輯讀從4000w到50w,物理讀30w基本沒有什麼大的變化,沒有內存排序。

到這裏基本優化完畢,24s查詢出4w條數據,我也是拼了老命了。針對大表全掃描,從謂詞信息看過濾條件是filter("PROCESSID" IS NOT NULL),因此也好像顯得無能爲力。

算了就此做罷。

測試驗證正確性

最簡單的辦法就是到測試環境中將修改SQL查詢的數據同原SQL查詢的數據作minus,而後獲得以下:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

其中t表是針對修改後SQL作的臨時表,驗證結論正確無誤。

ok!搞定收工!!!

相關文章
相關標籤/搜索