日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

一条SQL引发的“血案”:与SQL优化相关的4个案例

發(fā)布時(shí)間:2025/3/15 数据库 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一条SQL引发的“血案”:与SQL优化相关的4个案例 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.


導(dǎo)讀:筆者早年間從事了多年開發(fā)工作,后因個(gè)人興趣轉(zhuǎn)做數(shù)據(jù)庫。在長期的工作實(shí)踐中,看到了數(shù)據(jù)庫工作(特別是SQL優(yōu)化)面臨的種種問題。本文通過幾個(gè)案例探討一下SQL優(yōu)化的相關(guān)問題。

作者:馬立和 高振嬌 韓鋒

來源:大數(shù)據(jù)DT(ID:hzdashuju)

案例01 一條SQL引發(fā)的“血案”

1. 案例說明

某大型電商公司數(shù)據(jù)倉庫系統(tǒng),正常情況下每天0~9點(diǎn)會(huì)執(zhí)行大量作業(yè),生成前一天的業(yè)務(wù)報(bào)表,供管理層分析使用。但某天早晨6點(diǎn)開始,監(jiān)控人員就頻繁收到業(yè)務(wù)報(bào)警,大批業(yè)務(wù)報(bào)表突然出現(xiàn)大面積延遲。原本8點(diǎn)前就應(yīng)跑出的報(bào)表,一直持續(xù)到10點(diǎn)仍然沒有結(jié)果。公司領(lǐng)導(dǎo)非常重視,嚴(yán)令在11點(diǎn)前必須解決問題。

DBA緊急介入處理,通過TOP命令查看到某個(gè)進(jìn)程占用了大量資源,殺掉后不久還會(huì)再次出現(xiàn)。經(jīng)與開發(fā)人員溝通,這是由于調(diào)度機(jī)制所致,非正常結(jié)束的作業(yè)會(huì)反復(fù)執(zhí)行。

暫時(shí)設(shè)置該作業(yè)無效,并從腳本中排查可疑SQL。同時(shí)對(duì)比從線上收集的ASH/AWR報(bào)告,最終定位到某條SQL比較可疑。

經(jīng)與開發(fā)人員確認(rèn)系一新增功能,因上線緊急,只做了簡(jiǎn)單的功能測(cè)試。正是因?yàn)檫@一條SQL,導(dǎo)致整個(gè)系統(tǒng)運(yùn)行緩慢,大量作業(yè)受到影響,修改SQL后系統(tǒng)恢復(fù)正常。

  • 具體分析

SELECT?/*+?INDEX?(A1?xxxxx)?*/?SUM(A2.CRKSL),??SUM(A2.CRKSL*A2.DJ)?... FROM?xxxx?A2,?xxxx?A1? WHERE?A2.CRKFLAG=xxx?AND?A2.CDATE>=xxx?AND?A2.CDATE<xxx;

這是一個(gè)很典型的兩表關(guān)聯(lián)語句,兩張表的數(shù)據(jù)量都較大。下面來看看執(zhí)行計(jì)劃,如圖1-1所示。

執(zhí)行計(jì)劃觸目驚心,優(yōu)化器評(píng)估返回的數(shù)據(jù)量為3505T條記錄,計(jì)劃返回量127P字節(jié),總成本9890G,返回時(shí)間999:59:59。

▲圖1-1 執(zhí)行計(jì)劃

  • 分析結(jié)論

從執(zhí)行計(jì)劃中可見,兩表關(guān)聯(lián)使用了笛卡兒積的關(guān)聯(lián)方式。我們知道笛卡兒連接是指兩表沒有任何條件限制的連接查詢。一般情況下應(yīng)盡量避免笛卡兒積,除非某些特殊場(chǎng)合,否則再強(qiáng)大的數(shù)據(jù)庫也無法處理。

這是一個(gè)典型的多表關(guān)聯(lián)缺乏連接條件,導(dǎo)致笛卡兒積,引發(fā)性能問題的案例。

2. 給我們的啟示

從案例本身來講并沒有什么特別之處,不過是開發(fā)人員疏忽導(dǎo)致了一條質(zhì)量很差的SQL。但從更深層次來講,這個(gè)案例可以給我們帶來如下啟示。

  • 開發(fā)人員的一個(gè)疏忽造成了嚴(yán)重的后果,原來數(shù)據(jù)庫竟是如此的脆弱。需要對(duì)數(shù)據(jù)庫保持“敬畏”之心。

  • 電腦不是人腦,它不知道你的需求是什么,只能根據(jù)寫好的邏輯進(jìn)行處理。

  • 不要去責(zé)怪開發(fā)人員,誰都會(huì)犯錯(cuò)誤,關(guān)鍵是如何從制度上保證不再發(fā)生類似的問題。

3. 解決之道

1)SQL開發(fā)規(guī)范

加強(qiáng)對(duì)數(shù)據(jù)庫開發(fā)人員的培訓(xùn)工作,提高其對(duì)數(shù)據(jù)庫的理解能力和SQL開發(fā)水平。將部分SQL運(yùn)行檢查的職責(zé)前置,在開發(fā)階段就能規(guī)避很多問題。要向開發(fā)人員灌輸SQL優(yōu)化的思想,在工作中逐步積累,這樣才能提高公司整體開發(fā)質(zhì)量,也可以避免很多低級(jí)錯(cuò)誤。

2)SQL Review制度

對(duì)于SQL Review,怎么強(qiáng)調(diào)都不過分。從業(yè)內(nèi)來看,很多公司也都在自己的開發(fā)流程中納入了這個(gè)環(huán)節(jié),甚至列入考評(píng)范圍,對(duì)其重視程度可見一斑。其常見典型做法是利用SQL分析引擎(商用或自研)進(jìn)行分析或采取半人工的方式進(jìn)行審核。審核后的結(jié)果可作為持續(xù)改進(jìn)的依據(jù)。

SQL Review的中間結(jié)果可以保留,作為系統(tǒng)上線后的對(duì)比分析依據(jù),進(jìn)而可將SQL的審核、優(yōu)化、管理等功能集成起來,完成對(duì)SQL整個(gè)生命周期的管理。

3)限流/資源控制

有些數(shù)據(jù)庫提供了豐富的資源限制功能,可以從多個(gè)維度限制會(huì)話對(duì)資源(CPU、MEMORY、IO)的使用,可避免發(fā)生單個(gè)會(huì)話影響整個(gè)數(shù)據(jù)庫的運(yùn)行狀態(tài)。

對(duì)于一些開源數(shù)據(jù)庫,部分技術(shù)實(shí)力較強(qiáng)的公司還通過對(duì)內(nèi)核的修改實(shí)現(xiàn)了限流功能,控制資源消耗較多的SQL運(yùn)行數(shù)量,從而避免拖慢數(shù)據(jù)庫的整體運(yùn)行。

案例02 糟糕的結(jié)構(gòu)設(shè)計(jì)帶來的問題

1. 案例說明

這是某公司后臺(tái)的ERP系統(tǒng),系統(tǒng)已經(jīng)上線運(yùn)行了10多年。隨著時(shí)間的推移,累積的數(shù)據(jù)量越來越大。隨著公司業(yè)務(wù)量的不斷增加,數(shù)據(jù)庫系統(tǒng)運(yùn)行緩慢的問題日益凸顯。

為提高運(yùn)行效率,公司計(jì)劃有針對(duì)性地對(duì)部分大表進(jìn)行數(shù)據(jù)清理。在DBA對(duì)某個(gè)大表進(jìn)行清理時(shí)出現(xiàn)了問題。這個(gè)表本身有數(shù)百吉字節(jié),按照指定的清理規(guī)則只需要根據(jù)主鍵字段范圍(運(yùn)算符為>=)選擇出一定比例(不超過10%)的數(shù)據(jù)進(jìn)行清理即可。

但在實(shí)際使用中發(fā)現(xiàn),該SQL是全表掃描,執(zhí)行時(shí)間大大超出預(yù)期。DBA嘗試使用強(qiáng)制指定索引方式清理數(shù)據(jù),依然無效,整個(gè)SQL語句的執(zhí)行效率達(dá)不到要求。為了避免影響正常業(yè)務(wù)運(yùn)行,不得不將此次清理工作放在半夜進(jìn)行,還需要協(xié)調(diào)庫房等諸多單位進(jìn)行配合,嚴(yán)重影響正常業(yè)務(wù)運(yùn)行。

為了盡量減少對(duì)業(yè)務(wù)的影響,DBA求助筆者幫助協(xié)同分析。這套ERP系統(tǒng)是由第三方公司開發(fā)的,歷史很久遠(yuǎn),相關(guān)的數(shù)據(jù)字典等信息都已經(jīng)找不到了,只能從純數(shù)據(jù)庫的角度進(jìn)行分析。這是一個(gè)普通表(非分區(qū)表),按照主鍵字段的范圍查詢一批記錄并進(jìn)行清理。

按照正常理解,執(zhí)行索引范圍掃描應(yīng)該是效率較高的一種處理方式,但實(shí)際情況都是全表掃描。進(jìn)一步分析發(fā)現(xiàn),該表的主鍵是沒有業(yè)務(wù)含義的,僅僅是自增長的數(shù)據(jù),其來源是一個(gè)序列。

但奇怪的是,這個(gè)主鍵字段的類型是變長文本類型,而不是通常的數(shù)字類型。當(dāng)初定義該字段類型的依據(jù),現(xiàn)在已經(jīng)無從考證,但實(shí)驗(yàn)表明正是這個(gè)字段的類型“異常”,導(dǎo)致了錯(cuò)誤的執(zhí)行路徑。

下面通過一個(gè)實(shí)驗(yàn)重現(xiàn)這個(gè)問題。

1)數(shù)據(jù)準(zhǔn)備

兩個(gè)表的數(shù)據(jù)類型相似(只是ID字段類型不同),各插入了320萬數(shù)據(jù),ID字段范圍為1~3200000。

create?table?t1?as?select?*?from?dba_objects?where?1=0; alter?table?t1?add?id?int?primary?key; create?table?t2?as?select?*?from?dba_objects?where?1=0; alter?table?t2?add?id?varchar2(10)?primary?key;insert?into?t1? select?'test','test','test',rownum,rownum,'test',sysdate,sysdate,'test','test','','','',rownum? from?dual? connect?by?rownum<=3200000; insert?into?t2? select?'test','test','test',rownum,rownum,'test',sysdate,sysdate,'test','test','','','',rownum? from?dual? connect?by?rownum<=3200000; commit; execdbms_stats.gather_table_stats(ownname?=>?'hf',tabname?=>?'t1',cascade?=>true,estimate_percent?=>?100); execdbms_stats.gather_table_stats(ownname?=>?'hf',tabname?=>?'t2',cascade?=>true,estimate_percent?=>?100);

2)模擬場(chǎng)景

相關(guān)代碼如下:

select?*?from?t1?where?id>=?3199990; 11?rows?selected. -------------------------------------------------------------------------------- |?Id?|?Operation????????????????|?Name???????|Rows?|Bytes|Cost?(%CPU)|??Time????| --------------------------------------------------------------------------------- |??0?|?SELECT?STATEMENT?????????|????????????|?11??|?693?|???4??(0)?|?00:00:01?| |??1?|?TABLE?ACCESS?BY?INDEX?ROWID|?T1?????????|?11??|?693?|???4??(0)?|?00:00:01?| |*?2?|?INDEX?RANGE?SCAN?????????|SYS_C0025294|?11??|?????|???3??(0)?|?00:00:01?| --------------------------------------------------------------------------------- Statistics ---------------------------------------------------------- 1??recursive?calls 0??db?block?gets 6??consistent?gets 0??physical?reads

對(duì)于普通的采用數(shù)值類型的字段,范圍查詢就是正常的索引范圍掃描,執(zhí)行效率很高。

select?*?from?t2?where?id>=?'3199990'; 755565?rows?selected. -------------------------------------------------------------------------- |?Id??|?Operation?????????|?Name?|?Rows??|?Bytes?|?Cost?(%CPU)|?Time?????| -------------------------------------------------------------------------- |???0?|?SELECT?STATEMENT??|??????|??2417K|???149M|??8927???(2)|?00:01:48?| |*??1?|??TABLE?ACCESS?FULL|?T2???|??2417K|???149M|??8927???(2)|?00:01:48?| -------------------------------------------------------------------------- Statistics ---------------------------------------------------------- 1??recursive?calls 0??db?block?gets 82568??consistent?gets 0??physical?reads

對(duì)于文本類型字段的表,范圍查詢就是對(duì)應(yīng)的全表掃描,效率較低是顯而易見的。

3)分析結(jié)論

  • 字符類型在索引中是“亂序”的,這是因?yàn)樽址愋偷呐判蚍绞脚c我們的預(yù)期不同。從“select * from t2 where id>= '3199990'”執(zhí)行返回755 565條記錄可見,不是直觀上的10條記錄。這也是當(dāng)初在做表設(shè)計(jì)時(shí),開發(fā)人員沒有注意的問題。

  • 字符類型還導(dǎo)致了聚簇因子很大,原因是插入順序與排序順序不同。詳細(xì)點(diǎn)說,就是按照數(shù)字類型插入(1..3200000),按字符類型('1'...'32000000')t排序。

select?table_name,index_name,leaf_blocks,num_rows,clustering_factor from?user_indexes where?table_name?in?('T1','T2'); TABLE_NAME?????????INDEX_NAME??????LEAF_BLOCKS???NUM_ROWS????CLUSTERING_FACTOR --------------?--------------?----------------?----------?--------------------- T1???????????????SYS_C0025294?????????????6275????3200000?????????????????31520 T2???????????????SYS_C0025295????????????13271????3200000????????????????632615
  • 在對(duì)字符類型使用大于運(yùn)算符時(shí),會(huì)導(dǎo)致優(yōu)化器認(rèn)為需要掃描索引大部分?jǐn)?shù)據(jù)且聚簇因子很大,最終導(dǎo)致棄用索引掃描而改用全表掃描方式。

4)解決方法

具體的解決方法如下:

select?*?from?t2?where?id?between?'3199990'?and?'3200000'; -------------------------------------------------------------------------------- |?Id??|?Operation?????????????????|?Name?????????|Rows|Bytes?|Cost(%CPU)|?Time???| -------------------------------------------------------------------------------- |???0?|?SELECT?STATEMENT??????????|?????????????|???6|??390?|???5?(0)|00:00:01| |???1?|??TABLE?ACCESS?BY?INDEX?ROWID|?T2???????????|???6|??390?|???5?(0)|00:00:01| |*??2?|???INDEX?RANGE?SCAN????????|?SYS_C0025295?|???6|??????|???3?(0)|00:00:01| -------------------------------------------------------------------------------- Statistics ---------------------------------------------------------- 1??recursive?calls 0??db?block?gets 13??consistent?gets 0??physical?reads

將SQL語句由開放區(qū)間掃描(>=),修改為封閉區(qū)間(between xxx and max_value)。使得數(shù)據(jù)在索引局部順序是“對(duì)的”。如果采用這種方式仍然走全表掃描,還可以進(jìn)一步細(xì)化分段或者采用“逐條提取+批綁定”的方法。

2. 給我們的啟示

這是一個(gè)典型的由不好的數(shù)據(jù)類型帶來的執(zhí)行計(jì)劃異常的例子。它給我們帶來如下啟示:

  • 糟糕的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)往往是致命的,后期的優(yōu)化只是補(bǔ)救措施。只有從源頭上加以杜絕,才是優(yōu)化的根本。

  • 在設(shè)計(jì)初期能引入數(shù)據(jù)庫審核,可以起到很好的作用。

案例03 規(guī)范SQL寫法好處多

1. 案例說明

某大型電商公司數(shù)據(jù)倉庫系統(tǒng),開發(fā)人員反映作業(yè)運(yùn)行緩慢。經(jīng)檢查是一個(gè)新增業(yè)務(wù)中某條SQL語句導(dǎo)致。經(jīng)分析是非標(biāo)準(zhǔn)的SQL引起優(yōu)化器判斷異常,將其修改成標(biāo)準(zhǔn)寫法后,SQL恢復(fù)正常。

1)具體分析

看下面的代碼:

select?...?from?... where ((?order_creation_date>=?to_date(20120208,'yyyy-mm-dd')?and?order_creation_date<to_date(20120209,'yyyy-mm-dd'))? or(?send_date>=?to_date(20120208,'yyyy-mm-dd')?and?send_date<to_date(20120209, 'yyyy-mm-dd')) ) andnvl(a.bd_id,0)?=?1 -------------------------------------------------------------------------------- |??Id?|?Operation??????????????|?Name???|Cost?(%CPU)|?Time???|Pstart?|?Pstop?| -------------------------------------------------------------------------------- |???0?|?SELECT?STATEMENT???????|????????|?2470K(100)|????????|???????|???????| |???1?|??SORT?GROUP?BY?????????|????????|???????????|????????|???????|???????| |???2?|???TABLE?ACCESS?BY?GLOBAL?INDEX?ROWID|??XXXX??|?????5?(0)?|?00:00:01?|?ROW?L?|?ROW?L?| |???3?|????NESTED?LOOPS?????????|????????|?2470K?(1)?|?08:14:11?|???????|???????| |???4?|?????VIEW???????????????|VW_NSO_1|?2470K?(1)?|?08:14:10?|???????|???????| |???5?|??????FILTER????????????|????????|???????????|??????????|???????|???????| |???6?|???????HASH?GROUP?BY????|????????|??2470K?(1)|?08:14:10?|???????|???????| |???7?|????????TABLE?ACCESS?BY?GLOBAL?INDEX?ROWID?|??XXXX??|??????5?(0)|?00:00:01?|?ROW?L?|?ROW?L?| |???8?|?????????NESTED?LOOPS????|????????|??2470K?(1)|?08:14:10?|???????|???????| |???9?|??????????SORT?UNIQUE????|????????|??2340K?(2)|?07:48:11?|???????|???????| |??10?|???????????PARTITION?RANGE?ALL??|????????|??2340K?(2)|?07:48:11?|????1??|????92?| |??11?|????????????TABLE?ACCESS?FULL|??XXXX??|??2340K?(2)|?07:48:11?|????1??|????92?| |??12?|??????????INDEX?RANGE?SCAN?|??XXXX??|??????3?(0)|?00:00:01?|???????|???????| |??13?|?????INDEX?RANGE?SCAN????|??XXXX??|??????3?(0)|?00:00:01?|???????|???????| --------------------------------------------------------------------------------

這個(gè)SQL中涉及的主要表是一個(gè)分區(qū)表,從執(zhí)行計(jì)劃(Pstart、Pstop)中可見,掃描了所有分區(qū),分區(qū)裁剪特性沒有起效。

2)解決方法

見下面的代碼:

select?... from?... where?order_creation_date?>=?to_date(20120208,'yyyy-mm-dd')?and?order_creation_date<to_date(20120209,'yyyy-mm-dd') union?all select?... from?... where send_date>=?to_date(20120208,'yyyy-mm-dd')?and?send_date<to_date(20120209,'yyyy-mm-dd')?and? nvl(a.bd_id,0)?=?5

嘗試通過引入union all來分解查詢,以便于優(yōu)化器做出更準(zhǔn)確的判斷。采用這個(gè)方法后,確實(shí)起效了,當(dāng)然不可避免會(huì)掃描兩遍表。

select?... from?... where ((?order_creation_date>=?to_date(20120208,'yyyymmdd')?and?order_creation_date<to_date(20120209,'yyyymmdd'))? or(?send_date>=?to_date(20120208,'yyyymmdd')?and?send_date<to_date(20120209,'yyyymmdd')) ); -------------------------------------------------------------------------------- |??Id???|?Operation???????????|?Name?|?Cost(%CPU)|Time??????|?Pstart??|?Pstop???| -------------------------------------------------------------------------------- |?????0?|?SELECT?STATEMENT????|??????|??42358?(1)|?00:08:29?|?????????|?????????| |?????1?|??SORT?AGGREGATE?????|??????|???????????|??????????|?????????|?????????| |?????2?|???CONCATENATION?????|??????|???????????|??????????|?????????|?????????| |?????3?|????PARTITION?RANGE?SINGLE|??????|??17393?(1)|?00:03:29?|??????57?|?????57?| |*????4?|?????TABLE?ACCESS?FULL|?XXXX?|??17393?(1)|?00:03:29?|??????57?|?????57?| |*????5?|????TABLE?ACCESS?BY?GLOBAL?INDEX?ROWID?|?XXXX?|??24966?(1)|?00:05:00?|???ROWID?|??ROWID?| |*????6?|?????INDEX?RANGE?SCAN??|?XXXX?|????658?(1)|?00:00:08?|?????????|?????????| ---------------------------------------------------------------------------------

通過調(diào)整日期FORMAT格式,優(yōu)化器很精準(zhǔn)地判斷了分區(qū)(Pstart=57、Pstop=57),整體SQL性能得到了很大的提高,作業(yè)運(yùn)行時(shí)間從8個(gè)多小時(shí)縮減到8分鐘。

3)分析結(jié)論

對(duì)于非標(biāo)準(zhǔn)的日期格式,Oracle在復(fù)雜邏輯判斷的情況下分區(qū)裁剪特性無法識(shí)別,不起作用。這種情況下,會(huì)走全表掃描,結(jié)果是正確的,但是執(zhí)行效率會(huì)很低。通過使用union all,簡(jiǎn)化了條件判斷。使得Oracle在非保準(zhǔn)日期格式下也能使用分區(qū)裁剪特性,但最佳修改方式還是規(guī)范SQL的寫法。

2. 給我們的啟示

  • 規(guī)范的SQL寫法,不但利于提高代碼可讀性,還有利于優(yōu)化器生成更優(yōu)的執(zhí)行計(jì)劃。

  • 分區(qū)功能是Oracle應(yīng)對(duì)大數(shù)據(jù)的利器,但在使用中要注意是否真正會(huì)用到分區(qū)特性;否則,可能適得其反,使用分區(qū)會(huì)導(dǎo)致效率更差。

案例04 “月底難過”

1. 案例說明

某大型電商公司數(shù)據(jù)倉庫系統(tǒng)經(jīng)常出現(xiàn)在月底運(yùn)行緩慢的情況,但在平時(shí)系統(tǒng)運(yùn)行卻非常正常。這是因?yàn)樵碌淄性聢?bào)等大批量作業(yè)運(yùn)行,而就在這個(gè)時(shí)間點(diǎn)上,常常會(huì)出現(xiàn)緩慢情況,所以業(yè)務(wù)人員一到月底就非常緊張。這也成了一個(gè)老大難問題,困擾了很長時(shí)間。

DBA介入處理,發(fā)現(xiàn)一個(gè)很奇怪的現(xiàn)象:某條主要SQL是造成執(zhí)行緩慢的主因,其執(zhí)行計(jì)劃是不確定的,也就是說因?yàn)閳?zhí)行計(jì)劃的改變,導(dǎo)致其運(yùn)行效率不同。而往往較差的執(zhí)行計(jì)劃發(fā)生在月底幾天,且由于月底大批作業(yè)的影響,整體性能比較飽和,更突顯了這個(gè)問題。

針對(duì)某個(gè)出現(xiàn)問題的時(shí)間段做了進(jìn)一步分析,結(jié)果表明是由于統(tǒng)計(jì)信息的缺失導(dǎo)致了優(yōu)化器產(chǎn)生了較差的執(zhí)行計(jì)劃,并據(jù)此指定了人工策略,徹底解決了這個(gè)問題。

1)具體分析

先來看下面的代碼:

select... from?xxx?a?join?xxx?b?on?a.order_id?=?b.lyywzdid left?join?xxx?c?on?b.gysid?=?c.gysid whereb.cdate>=?to_date('2012-03-31',?'yyyy-mm-dd')?–?3?and?... a.send_date>=?to_date('2012-03-31',?'yyyy-mm-dd')?-?1?and?a.send_date<to_date('2012-03-31',?'yyyy-mm-dd'); -------------------------------------------------------------------------------- |Id??|?Operation??????????|Name??|??Rows??|??Bytes??|?Cost?(%CPU)?|Pstart|Pstop| -------------------------------------------------------------------------------- |??0?|?SELECT?STATEMENT???|??????|??????1?|?????104?|??????9743(1)|??????|?????| |??1?|??HASH?JOIN?OUTER???|??????|??????1?|?????104?|??????9743(1)|??????|?????| |??2?|???TABLE?ACCESS?BY?LOCAL?INDEX?ROWID|?XXXX?|??????1?|??????22?|?????????0(0)|?1189?|?1189| |??3?|????NESTED?LOOPS????|??????|??????1?|??????94?|??????9739(1)|??????|?????| |??4?|?????PARTITION?RANGE?ITERATOR????|??????|???1032?|???74304?|??????9739(1)|??123?|?518?| |??5?|??????TABLE?ACCESS?FULL?|?XXXX?|???1032?|???74304?|??????9739(1)|??123?|?518?| |??6?|?????PARTITION?RANGE?SINGLE|??????|??????1?|?????????|?????????0(0)|?1189?|?1189?| |??7?|??????INDEX?RANGE?SCAN?|?XXXX?|??????1?|?????????|?????????0(0)|?1189?|?1189?| |??8?|???TABLE?ACCESS?FULL|?XXXX?|????183?|????1830?|?????????3(0)|??????|?????| --------------------------------------------------------------------------------

執(zhí)行計(jì)劃中,多表關(guān)聯(lián)使用了嵌套循環(huán),這點(diǎn)對(duì)于OLAP系統(tǒng)來說是比較少見的。一般優(yōu)化器更傾向于使用SM和HJ。進(jìn)一步檢查發(fā)現(xiàn)其成本竟然是0,怪不得優(yōu)化器使用了嵌套循環(huán)。

2)深入分析

檢查發(fā)現(xiàn)索引數(shù)據(jù)統(tǒng)計(jì)信息異常,這是分區(qū)索引,僅兩天的分區(qū)統(tǒng)計(jì)信息都是0。導(dǎo)致優(yōu)化器認(rèn)為嵌套循環(huán)的執(zhí)行效率更高,而不是使用哈希連接。結(jié)合業(yè)務(wù)發(fā)現(xiàn),月底是業(yè)務(wù)高峰期,對(duì)于系統(tǒng)統(tǒng)計(jì)信息的作業(yè)收集,在指定的時(shí)間窗口內(nèi)無法完成。最后導(dǎo)致統(tǒng)計(jì)信息不完整,優(yōu)化器采用了錯(cuò)誤的執(zhí)行計(jì)劃。

3)解決方法

解決的代碼如下:

exec?dbms_stats.gather_index_stats(ownname=>'xxx',?indname=>'xxx',partname=>'PART_xxx',?estimate_percent?=>?10);

分析完對(duì)象的統(tǒng)計(jì)信息即恢復(fù)正常。

2. 給我們的啟示

  • 統(tǒng)計(jì)信息是優(yōu)化器優(yōu)化的重要參考依據(jù),一個(gè)完整、準(zhǔn)確的統(tǒng)計(jì)信息是必要條件。往往在優(yōu)化過程中,第一步就是查看相關(guān)對(duì)象的統(tǒng)計(jì)信息。

  • 分區(qū)機(jī)制是Oracle針對(duì)大數(shù)據(jù)的重要解決手段,但也很容易造成所謂“放大效應(yīng)”。即對(duì)于普通表而言,統(tǒng)計(jì)信息更新不及時(shí)可能不會(huì)導(dǎo)致執(zhí)行計(jì)劃偏差過大;但對(duì)于分區(qū)表、索引來說,很容易出現(xiàn)因更新不及時(shí)出現(xiàn)0的情況,進(jìn)而導(dǎo)致執(zhí)行計(jì)劃產(chǎn)生嚴(yán)重偏差。

關(guān)于作者:馬立和,研究員級(jí)高工,哈爾濱學(xué)院教師。主要研究方向數(shù)據(jù)庫、圖形圖像處理。

高振嬌,對(duì)金融行業(yè)的數(shù)據(jù)庫具有豐富的運(yùn)維管理經(jīng)驗(yàn)。熟悉傳統(tǒng)關(guān)系型數(shù)據(jù)庫 Oracle 、MySQL,對(duì)NoSQL 以及 NewSQL 具有濃厚的興趣。同時(shí)對(duì)自動(dòng)化運(yùn)維也有較為深刻的理解,是 Themis 開源數(shù)據(jù)庫審核平臺(tái)的核心成員。

韓鋒,CCIA(中國計(jì)算機(jī)協(xié)會(huì))常務(wù)理事,Oracle ACE,騰訊云TVP,dbaplus等多家社群創(chuàng)始人或?qū)<覉F(tuán)成員。有多年一線數(shù)據(jù)庫架構(gòu)、軟件研發(fā)、產(chǎn)品設(shè)計(jì)、團(tuán)隊(duì)管理經(jīng)驗(yàn)。

本文摘編自《數(shù)據(jù)庫高效優(yōu)化:架構(gòu)、規(guī)范與SQL技巧》,經(jīng)出版方授權(quán)發(fā)布。

延伸閱讀《數(shù)據(jù)庫高效優(yōu)化》

點(diǎn)擊上圖了解及購買

轉(zhuǎn)載請(qǐng)聯(lián)系微信:DoctorData

推薦語:本書以大量案例為依托,系統(tǒng)講解了SQL語句優(yōu)化的原理、方法及技術(shù)要點(diǎn),尤為注重實(shí)踐,在章節(jié)中引入了大量的案例,便于學(xué)習(xí)者實(shí)踐、測(cè)試,反復(fù)揣摩。?

劃重點(diǎn)????

干貨直達(dá)????

  • 阿里巴巴B2B電商算法首次對(duì)外公開

  • 長期豪賭人工智能,Alphabet是怎樣一步一步偷偷改變世界的?

  • 詳解自然語言處理5大語義分析技術(shù)及14類應(yīng)用(建議收藏)

  • 馬太效應(yīng)和冪律分布是怎么回事?終于有人講明白了

更多精彩????

在公眾號(hào)對(duì)話框輸入以下關(guān)鍵詞

查看更多優(yōu)質(zhì)內(nèi)容!

PPT?|?讀書?|?書單?|?硬核?|?干貨?|?講明白?|?神操作

大數(shù)據(jù)?|?云計(jì)算?|?數(shù)據(jù)庫?|?Python?|?可視化

AI?|?人工智能?|?機(jī)器學(xué)習(xí)?|?深度學(xué)習(xí)?|?NLP

5G?|?中臺(tái)?|?用戶畫像?|?1024?|?數(shù)學(xué)?|?算法?|?數(shù)字孿生

據(jù)統(tǒng)計(jì),99%的大咖都完成了這個(gè)神操作

????

總結(jié)

以上是生活随笔為你收集整理的一条SQL引发的“血案”:与SQL优化相关的4个案例的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。