天天看點

從執行計劃的預估行數看執行計劃是否正确

從執行計劃的預估行數可以看出執行計劃是否正确,作為優化的你曾經注意到了麼?
今天在監控系統垃圾sql語句的時候發現一個sql語句跑了10個小時了,憑直覺這個sql肯定哪裡出現問題了,好吧,老規矩,先看看記憶體中執行計劃和具體的sql語句吧,這裡的sql語句:

INSERT /*+ append */ INTO CJG_MID_ACCT(ACCT,PARTY_NO,CHINESE_NAME,DATE_OPENED,RMB_CURRENT_BAL,BILL_DATE,RMB_CRLIM,AI_LAST,PSTL_CD,BIRTHDAY,GENDER,APPLICATION_NO,RMB_BLOCK_CODE_1,RMB_BLOCK_CODE_1_MEMO,RMB_BLOCK_CODE_2,RMB_BLOCK_CODE_2_MEMO,USD_CURRENT_BAL,USD_BLOCK_CODE_1,USD_BLOCK_CODE_1_MEMO,USD_BLOCK_CODE_2,USD_BLOCK_CODE_2_MEMO,DATE_ACTIVE,CURRENT_CARD_CNT,EDUCATION_DEGREE,INDUSTRY_TYPE,COMPANY_ATTRIBUTE,SALES_CODE,ANNUAL_INCOME,SOURCE_CODE,UTILIZATION_FLAG,CURRENT_FLAG,MOB,AGE,SFNAME,CITY,AI_FIRST) SELECT A.*,C.ACCT_ACTIVE_DATE AS DATE_ACTIVE, B.CURRENT_CARD_CNT,D.EDUCATION_DEGREE, D.INDUSTRY_TYPE,D.COMPANY_ATTRIBUTE,D.SALES_CODE,D.ANNUAL_INCOME,D.SOURCE_CODE, 'N' AS CURRENT_FLAG,'N' AS CURRENT_CURRENT_FLAG, TRUNC(MONTHS_BETWEEN(TO_DATE(SUBSTR(:B1 ,1,6),'yyyymm'),TO_DATE(SUBSTR(DATE_OPENED,1,6),'yyyymm')),0) AS MOB, FLOOR((TO_DATE(:B1 ,'yyyymmdd')-TO_DATE(A.BIRTHDAY,'yyyymmdd'))/365) AS AGE, NVL(E.SFNAME,'其他') AS SFNAME,NVL(E.QXNAME,'其他') AS CITY,D.AI AS AI_FIRST FROM TEMP_MID_ACCT_TMP02 A LEFT JOIN TEMP_MID_ACCT_CARDCNT B ON A.ACCT=B.ACCT LEFT JOIN TEMP_MID_ACCT_ACTIVED C ON A.ACCT=C.POST_TO_ACCT LEFT JOIN RISKREPT.RKO_CDM_PROCESS D ON A.APPLICATION_NO=D.APPLICATION_NO LEFT JOIN DIC_ZIP_CODE E ON SUBSTR(A.PSTL_CD,1,4)=E.ZIPCODE;

執行:SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('djmga872c636s',0,'advanced')); 得到執行計劃如下圖:

從執行計劃的預估行數看執行計劃是否正确

從執行計劃其實就可以看出問題來了,我們這裡再多走一步吧,先看看曆史sql中,這個語句跑了多久呢?

select * from XB_SQL_MONITOR_LHR a where a.SQL_ID='djmga872c636s';

從執行計劃的預估行數看執行計劃是否正确

可以看出曆史sql中這條語句僅僅跑了45分鐘,好吧,我們再看看曆史sql的執行計劃吧:

select DBMS_XPLAN.display_awr(sql_id => 'djmga872c636s') from dual;

從執行計劃的預估行數看執行計劃是否正确

這裡我截取了其中一個我認為正确的執行計劃,大家從附件中可以看到有很多曆史執行計劃,,好了,,,,廢話少說吧,,,,,,,,,

從執行計劃的預估行數看執行計劃是否正确

到這裡我們知道,這個sql的執行計劃改變過,正确的預估行數少的有2000行,多的有1000W+的資料,好吧,我們在從資料字典中具體檢視一下涉及到的表的具體行數吧,請看:

SELECT *

FROM vq_table_lhr a

WHERE a.TABLE_NAME IN ('TEMP_MID_ACCT_TMP02',

'TEMP_MID_ACCT_CARDCNT',

'TEMP_MID_ACCT_ACTIVED',

'RKO_CDM_PROCESS',

'DIC_ZIP_CODE')

;

從執行計劃的預估行數看執行計劃是否正确

可以看出,相關的5個表,最小的1M,最大的有31G,怎麼可能num_rows和blocks都為0呢?操蛋,,,,,oracle自動收集不智能,,,,,,當然也有可能是空表,高水位沒有降下來,沒事,我們收集統計資訊後再看看就知道了,執行:

BEGIN

dbms_stats.gather_table_stats(USER, 'DIC_ZIP_CODE',cascade => TRUE,degree => 8);

dbms_stats.gather_table_stats(USER, 'TEMP_MID_ACCT_ACTIVED',cascade => TRUE,degree => 8);

dbms_stats.gather_table_stats(USER, 'TEMP_MID_ACCT_CARDCNT',cascade => TRUE,degree => 8);

dbms_stats.gather_table_stats(USER, 'TEMP_MID_ACCT_TMP02',cascade => TRUE,degree => 8);

dbms_stats.gather_table_stats('RISKREPT', 'RKO_CDM_PROCESS',cascade => TRUE,degree => 20);

END;

表比較多,分析比較慢,,,等等等等,,,,,,不要急嘛,,,在神馬都是快節奏的今天我們更應該時不時的停下我們繁忙的腳步看看周邊美麗的風景的,,,,,,,千呼萬喚始出來,,,,,,,對表進行分析執行完成後,繼續檢視,行數和塊數:

從執行計劃的預估行數看執行計劃是否正确

好吧,大家可以看出num_rows和blocks都正确了,最大的表竟然有3500W+的資料,難怪跑這麼慢,同時也證明了之前我們猜測可能是表的高水位沒有降下來這個結論是錯誤的,我們再重新跑一跑之前的sql語句,捕捉一下執行計劃,偷懶一下,直接用plsql developer工具來看吧:

從執行計劃的預估行數看執行計劃是否正确

好了,應該沒有神馬問題了,,,,,,

1、有關CBO模式下,統計資訊一定要準确,不然可能出現N多不知神馬的問題,sql執行計劃瞎走,之前哥還遇到過統計資訊不準确導緻執行計劃走了笛卡爾積連接配接了,暈暈暈

從執行計劃的預估行數看執行計劃是否正确

,,,,,,啥事都有,,,,,,不過那個例子忘記截取下來了

2、另外,大家可以寫一個定時的job,對這一類統計資訊錯誤的表定時分析,或者如果表的資料量變化不是很大的情況下,我們可以鎖定這些臨時表的統計資訊,不讓系統的job對這些表重新分析

3、網上有大牛說我們看AWR報告的時候要有一雙敏銳的鷹眼,這裡我也提出看執行計劃的時候我們同樣應該具備一雙更敏銳的鷹眼,瞅一眼就知道哪裡的問題了,請看這隻眼:

從執行計劃的預估行數看執行計劃是否正确