成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

實戰(zhàn)ORACLE SQL優(yōu)化案例

IT那活兒 / 1528人閱讀
實戰(zhàn)ORACLE SQL優(yōu)化案例

CBO( Cost BasedOptimizer)優(yōu)化器是目前ORACLE默認使用的優(yōu)化器,它使用統(tǒng)計信息、查詢轉(zhuǎn)換等計算各種可能的訪問路徑成本,并生成多種備選執(zhí)行計劃,最終ORACLE選擇成本最低的作為最終執(zhí)行計劃。與舊的RBO(RuleBased Optimizer)相比,更加靈活,可根據(jù)實際情況選擇最佳執(zhí)行路徑。

但是,由于其自身非常復雜,CBO的限制以及存在的BUG非常多,這時,作為SQL開發(fā)和優(yōu)化人員,應該根據(jù)CBO特性,編寫高效語句,以避免踩坑CBO優(yōu)化器。

本文以三類常見的SQL優(yōu)化問題來探討基于ORACLE的高效SQL編寫和優(yōu)化。


[
ORACLE分頁查詢優(yōu)化之道
]


ORACLE中主要使用ROWNUM來實現(xiàn)TOP-N分頁查詢,分頁SQL編寫有如下規(guī)則:

  1. 分頁查詢一般需要排序,內(nèi)層查詢需要先ORDER BY。


  2. 如果查詢TOP–N行,需要兩層嵌套:內(nèi)層先排序,在外層嵌套查詢ROWNUM,并且同一層按照WHERE ROWNUM <或<=進行過濾。


  3. 如果查詢第M行到第N行,需要三層嵌套:內(nèi)層先排序,之后外層嵌套查詢ROWNUM并且將ROWNUM取別名,比如取別名為RN,且同一層按照WHERE ROWNUM <或<=進行過濾,之后最外層RN按照WHERE rn >或>=進行過濾。


分頁查詢優(yōu)化要點

分頁查詢的排序是高代價操作,如果不能避免排序,則需要所有結(jié)果集查詢完畢后進行排序操作后,才能進行分頁選擇。如果能夠避免排序,則可以充分使用到ORACLE分頁查詢的COUNTSTOPKEY算法,比如找前100行,則只要找到100行整條語句則可結(jié)束計算,這樣就可以提升分頁查詢效率了。很顯然,高效分頁查詢必須做到:

  1. 避免排序:通過創(chuàng)建索引

  2. 執(zhí)行計劃使用COUNT STOPKEY算法,進行分頁裁剪。


錯誤的分頁SQL寫法

分頁SQL編寫必須遵守查詢前面說的3個規(guī)則,如下例是錯誤的分頁語句寫法:


這條語句查詢前20行,應該使用兩層嵌套規(guī)則:最內(nèi)層排序,外層查詢rownum,并且在同一層用whererownum<或<=進行過濾。仔細分析這條語句,發(fā)現(xiàn)是兩層嵌套,但是不符合“并且在同一層用whererownum<或<=進行過濾”這個條件,此語句查詢rownum之后取了別名rn,在最外層進行rn過濾,可以從執(zhí)行計劃看到,走了全表掃描:


這里的表TM_TESTX_TEMP的列DONE_DATE有索引,但是因為使用了錯誤的分頁SQL寫法,導致執(zhí)行計劃無法使用COUNTSTOPKEY進行裁剪(執(zhí)行計劃中未出現(xiàn)COUNTSTOPKEY),這樣ORACLE需要按照條件查詢所有的結(jié)果集,從而走索引COST更大,最終走了全表掃描。


正確的分頁SQL寫法

如果按照規(guī)則進行SQL編寫,則可以完美進行高效分頁,<=分頁只需要2層嵌套,done_date列有索引,根據(jù)條件done_date>to_date(‘20150916’,‘YYYYMMDD’),只獲取前20行,可高效利用索引和COUNTSTOPKEY算法,改寫完成后使用索引降序掃描,執(zhí)行時間從1.72s到0.01s,邏輯IO從42648到59,效率提升百倍。如下所示:


語句改寫為外層取rownum的同時按照WHEREROWNUM <= 20進行過濾,而不是原來的在最外層進行過濾,符合分頁SQL編寫規(guī)則,執(zhí)行計劃變?yōu)椋?/span>


修改完后,可以看到根據(jù)ORDERBY DONE_DATE DESC,執(zhí)行計劃走了索引降序掃描,這樣避免了排序,并且使用到了COUNTSTOPKEY算法,找到前20行,則SQL運算結(jié)束,從而提高效率。


表關(guān)聯(lián)SQL分頁優(yōu)化

以上只是單表分頁查詢的高效SQL編寫和優(yōu)化思路,如果是多表關(guān)聯(lián)SQL分頁,也需要遵循分頁SQL編寫規(guī)則,優(yōu)化方式同樣是利用索引消除排序,并且能夠使用COUNTSTOPKEY算法,很顯然,要做到這些,必須以O(shè)RDERBY列所在表為驅(qū)動表,JOIN方式為NESTEDLOOPS,這樣可全部走索引并且可使用STOPKEY算法進行結(jié)果集裁剪,提高效率。如下例:


這條語句是test1和test2進行半連接,并按照test1的object_id列進行降序排列,最終返回test1的前10行數(shù)據(jù)。從子查詢關(guān)聯(lián)條件上看,按照object_id和object_name列關(guān)聯(lián),沒有額外的過濾條件,從語句結(jié)構(gòu)上看執(zhí)行計劃應該中表test1應該是全表掃描,如下所示:


很顯然,這不是最佳執(zhí)行計劃:沒有消除排序,兩表都是全表掃描,所有結(jié)果集返回后,才進行STOPKEY(SORTORDER BY STOPKEY)。前面已經(jīng)說過,對于表關(guān)聯(lián)的分頁查詢,應該用排序鍵所在的表為驅(qū)動表,JOIN方式為NESTEDLOOPS,并且消除排序,根據(jù)這個思想,應該在兩表的object_id列分別建立索引即可:

create indexidx_test1 on test1(object_id);

create indexidx_test2 on test2(object_id);


索引創(chuàng)建完畢后的執(zhí)行計劃如下:

現(xiàn)在的執(zhí)行計劃完全符合多表關(guān)聯(lián)分頁查詢優(yōu)化思路,以test1表為驅(qū)動表,消除排序,test1和test2之間走NESTEDLOOPS,可以從執(zhí)行計劃上看出,ID=4的步驟雖然E-ROWS估算為69444行,但是實際只找到10行,也就結(jié)束了,最終邏輯讀從原先的2184降低到15,大幅度提升了效率。


因為分頁查詢要利用到STOPKEY算法,就算除關(guān)聯(lián)條件外沒有額外的過濾條件,也可以通過索引來提升效率。


[
FILTER性能殺手問題
]


FILTER操作是執(zhí)行計劃中常見的操作,這種操作有兩種情況:

  1. 只有一個子節(jié)點,那么就是簡單過濾操作。

  2. 有多個子節(jié)點,那么就是類似NESTED LOOPS操作,只不過與NESTED LOOPS差別在于,F(xiàn)ILTER內(nèi)部會構(gòu)建HASH表,對于重復匹配的,不會再次進行循環(huán)查找,而是利用已有結(jié)果,提高效率。但是一旦重復匹配的較少,循環(huán)次數(shù)多,那么,F(xiàn)ILTER操作將是嚴重影響性能的操作,這是經(jīng)常導致性能問題的原因。


NOT IN子查詢中的FILTER

對于NOTIN子查詢,在11g之前經(jīng)常會出現(xiàn)性能問題,如下例SQL所示:

針對上面的NOTIN子查詢,如果子查詢object_id有NULL存在,則整個查詢都不會有結(jié)果,在11g之前,如果主表和子表的object_id未同時有NOTNULL約束,或都未加ISNOT NULL限制,則ORACLE會走FILTER。11g有新的ANTINA(NULLAWARE)優(yōu)化,可以對子查詢進行UNNEST查詢轉(zhuǎn)換,從而提高效率。


對于未UNNEST的子查詢,走了FILTER,有至少2個子節(jié)點,執(zhí)行計劃還有個特點就是Predicate謂詞部分有:B1這種類似綁定變量的東西,內(nèi)部操作走類似NESTEDLOOPS操作,執(zhí)行計劃如下:


可以從執(zhí)行計劃上看到,F(xiàn)ILTER有子節(jié)點ID=2和ID=3,并且ID=3部分出現(xiàn)綁定變量:B1,這是典型的NOTIN子查詢未UNNEST的執(zhí)行計劃,性能很差。

11g有NULLAWARE專門針對NOTIN問題進行優(yōu)化(要求參數(shù):_optimizer_squ_bottomup、_optimizer_null_aware_antijoin同時為true),如下所示:


11g中可以走HASHJOIN RIGHT ANTI NA,其中NA就是NULLAWARE的意思,邏輯讀從原先的23w降低到6105,效率提升明顯。如果在11g之前,針對這種SQL的優(yōu)化方式有:

  1. 子查詢選擇條件的列增加NOT NULL約束。如上SQL需要對anti_test1和anti_test2的object_id列增加NOT NULL約束。


  2. 改寫SQL:對子查詢選擇條件的列增加IS NOT NULL條件,如下所示:


  1. 改寫SQL:將NOT IN改為JOIN形式。如下所示:

NOT IN子查詢改為JOIN的等價形式必須是外連接+子查詢表對應的選擇條件ISNULL。


  1. 改寫SQL:將NOT IN子查詢改為NOT EXISTS子查詢。如下所示:

以上四種方式的執(zhí)行計劃都是可以走HASHJOIN RIGHT ANTI的正確計劃:



當然,如果NOTIN子查詢的確存在NULL,可能不返回結(jié)果,這種情況下是不可以用以上方式進行等價改寫的,只有在NOTIN子查詢肯定會返回結(jié)果,而且執(zhí)行計劃出現(xiàn)FILTER的時候才考慮以上方式進行優(yōu)化。


OR子查詢中的FILTER

再來看下常見的OR與子查詢連用情況,在實際優(yōu)化過程中,遇到OR與子查詢連用,一般都不能unnestsubquery了,這樣執(zhí)行計劃出現(xiàn)FILTER,可能會導致嚴重性能問題,OR與子查詢連用有兩種可能:

  1. condition or subquery

  2. subquery內(nèi)部包含or,如in (select … from tab where condition1 or condition 2)

如下例所示:


上面SQL的子查詢關(guān)聯(lián)條件包含OR,執(zhí)行計劃如下:


可以看到執(zhí)行計劃走FILTER,子查詢表DBA_OBJECTS_B被全表驅(qū)動9999次,邏輯讀10M,耗時35s,性能低下。其根本原因就是因為CBO對包含OR的子查詢此處沒有進行unnest,導致走了FILTER。當然,在不考慮改寫的情況下,可以對DBA_OBJECTS_B的OBJECT_ID和OBJECT_NAME分別建立索引,從而避免對DBA_OBJECTS_B進行上萬次的全表掃描來提高效率。如下:


現(xiàn)在邏輯讀從10M變?yōu)?341,執(zhí)行時間從從35s變?yōu)?.05s,這里建立索引還是沒有消除FILTER,索引被執(zhí)行9999次,很顯然,如果ID=3的結(jié)果行數(shù)增大,索引的掃描次數(shù)就會增多,這顯然是治標不治本的方式。


針對OR子查詢無法unnest導致走FILTER的問題,一般需要通過改寫,改寫思路如下:

  1. 將OR條件改為UNION或UNION ALL。

  2. 根據(jù)語義改寫,徹底消除OR條件。

根據(jù)以上優(yōu)化指導思想,這條語句可改寫為UNION形式,如下:



將OR條件改寫為兩條語句,使用UNION合并,最終查詢COUNT(*),執(zhí)行計劃如下:


現(xiàn)在的執(zhí)行計劃兩個子查詢語句都可以進行UNNEST,走HASHJOINSEMI,避免了FILTER操作,最終執(zhí)行時間從原來的35s變?yōu)?.04s,邏輯讀從10M減少為2550,子查詢都是執(zhí)行一次,也避免了建立索引,子查詢效率依賴于驅(qū)動表結(jié)果行數(shù)的目的。


下面再看一個徹底消除OR條件的改寫案例:

這里是NOTEXISTS子查詢帶OR條件,執(zhí)行計劃如下:


同樣,這里的執(zhí)行計劃走FILTER,耗時21s,邏輯讀468w,效率低下。如何徹底改寫消除OR條件呢?可以使用集合運算的思路,集合運算中NOT (A OR B) 等價于NOT A AND NOT B。則可以將OR條件改寫為AND條件:


執(zhí)行計劃如下:

將子查詢OR改寫為AND后,子查詢可以UNNEST,走HASHJOIN RIGHT ANTI,最終執(zhí)行時間從21s到0.03s,邏輯讀從468w到1450,效率提升明顯。


[
直方圖與綁定變量問題
]


直方圖與綁定變量問題是困擾SQL性能優(yōu)化的一個典型問題:一方面綁定變量是為了讓執(zhí)行計劃共享,從而減少或避免解析,但是如果一個列分布不均,傳入不同的值最佳執(zhí)行計劃應該不一樣,比如當status=’INVALID’的時候最佳執(zhí)行計劃是走索引,當status=’VALID’時候最佳執(zhí)行計劃是要求全表掃描,遇到這種情況,必須要再次窺視傳入的綁定變量值,才能走正確執(zhí)行計劃,因此,11G引入了AdaptiveCursorSharing(ACS)來解決這個問題,但是因為BUG多,一般情況下生產(chǎn)庫是建議關(guān)閉的。那么還能不能解決這個問題呢?在11.2及之后答案是肯定的。在11.2的時候,我們使用SQLPATCH來解決,這個類似以前的SQLPROFILE。如下例所示:


對于表T的STATUS列分布如下:

STATUS          COUNT(*)

------------------------

VALID              72398

INVALID                1


其中STATUS列有索引,顯然,傳入INVALID時候應該走索引,傳入VALID時候應該走全表掃描。如果關(guān)閉了ACS,則誰先執(zhí)行,后面的共享前面的執(zhí)行計劃。如下先執(zhí)行INVALID走索引:


但是再執(zhí)行VALID時候,還是一樣執(zhí)行計劃:


可以看出,現(xiàn)在返回行數(shù)從1行變成72396行,但是執(zhí)行計劃沒有變,根本原因是沒有再次對綁定變量進行PEEKING,PEEKING的值還是原來的INVALID。知道這點就好辦了,其實ACS的本質(zhì)是使用了HINTS:BIND_AWARE,那么在11.2時候使用SQLPATCH就可以了,如下所示:


11G寫法如下(dbms_sqldiag_internal這個是內(nèi)部存儲過程,一般不建議使用):

declare

l_sql_text clob;

begin

SELECT sql_fulltextINTO l_sql_text FROM v$sql WHERE sql_id = bbj7tdztdu843 AND ROWNUM< 2;

sys.dbms_sqldiag_internal.i_create_patch(sql_text  => l_sql_text,

hint_text => BIND_AWARE,

name      => bind_aware_bbj7tdztdu843

,description => test_sql_patch);

end ;

/


12.2及之后可以使用官方的DBMS_SQLDIAG.create_sql_patch:

DECLARE

  l VARCHAR2(32767);

BEGIN

  l :=SYS.DBMS_SQLDIAG.create_sql_patch(

   sql_id    => bbj7tdztdu843,

   hint_text => q[BIND_AWARE],

   name      => bind_aware_bbj7tdztdu843);

END;

/


在19c里測試,使用SQLPROFILE也是有效的,11g里使用SQLPROFILE無效。


這樣,我們先執(zhí)行INVALID,還是走索引:


但是再執(zhí)行VALID,可以看到,綁定變量以及窺視了,變成VALID,走了全表掃描,通過NOTE也可以看到走了SQLPATCH。



可以看出,使用SQLPATCH特性,可以很好地解決直方圖與綁定變量的問題。


總結(jié):本文通過“分頁查詢優(yōu)化”、“FILTER性能殺手”、“直方圖與綁定變量”這三個常見問題,探討CBO優(yōu)化器的特性以及通過編寫高質(zhì)量SQL語句來達到提升性能的目的。SQL語句性能,涉及的因素很多,如統(tǒng)計信息、索引等,更為重要的是,熟知優(yōu)化器特性,從而能夠編寫與CBO優(yōu)化器特性相匹配的SQL語句,這樣才能使用到CBO優(yōu)秀的特性,保證SQL語句的執(zhí)行性能。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/130153.html

相關(guān)文章

  • 數(shù)據(jù)庫收集 - 收藏集 - 掘金

    摘要:前言在使用加載數(shù)據(jù)數(shù)據(jù)庫常見的優(yōu)化操作后端掘金一索引將放第一位,不用說,這種優(yōu)化方式我們一直都在悄悄使用,那便是主鍵索引。 Redis 內(nèi)存壓縮實戰(zhàn) - 后端 - 掘金在討論Redis內(nèi)存壓縮的時候,我們需要了解一下幾個Redis的相關(guān)知識。 壓縮列表 ziplist Redis的ziplist是用一段連續(xù)的內(nèi)存來存儲列表數(shù)據(jù)的一個數(shù)據(jù)結(jié)構(gòu),它的結(jié)構(gòu)示例如下圖 zlbytes: 記錄整...

    Little_XM 評論0 收藏0
  • 做IT這幾年,我整理了這些干貨想要送給你!

    摘要:資源獲取方式根據(jù)下面的索引,大家可以選擇自己需要的資源,然后在松哥公眾號牧碼小子后臺回復對應的口令,就可以獲取到資源的百度云盤下載地址。公眾號二維碼如下另外本文會定期更新,松哥有新資源的時候會及時分享給大家,歡迎各位小伙伴保持關(guān)注。 沒有一條路是容易的,特別是轉(zhuǎn)行計算機這條路。 松哥接觸過很多轉(zhuǎn)行做開發(fā)的小伙伴,我了解到很多轉(zhuǎn)行人的不容易,記得松哥大二時剛剛決定轉(zhuǎn)行計算機,完全不知道這...

    王晗 評論0 收藏0
  • 新書推薦 |《PostgreSQL實戰(zhàn)》出版(提供樣章下載)

    摘要:作者譚峰張文升出版日期年月頁數(shù)頁定價元本書特色中國開源軟件推進聯(lián)盟分會特聘專家撰寫,國內(nèi)多位開源數(shù)據(jù)庫專家鼎力推薦。張文升中國開源軟件推進聯(lián)盟分會核心成員之一。 很高興《PostgreSQL實戰(zhàn)》一書終于出版,本書大體上系統(tǒng)總結(jié)了筆者 PostgreSQL DBA 職業(yè)生涯的經(jīng)驗總結(jié),本書的另一位作者張文升擁有豐富的PostgreSQL運維經(jīng)驗,目前就職于探探科技任首席PostgreS...

    Martin91 評論0 收藏0

發(fā)表評論

0條評論

IT那活兒

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<