一次ORA-01555問題分析,及SQL優化。


前言

客戶說:
我在數據庫上繼續運行昨日的腳本,但發現有個子過程在運行10個小時后報錯:
煩請協助看看。。。
錯誤碼是:ORA-01555: snapshot too old: rollback segment number 6 with name “_SYSSMU6$” too small
ORA-02063: preceding line from CLONE
這里寫圖片描述

分析

發生ORA-01555錯誤,一般是因為數據庫內部,有長SQL在運行,運行時間超過undo保存數據的時間。Clone庫undo保存數據的時間為:18000s。

根據錯誤提示,找到對應的SQL:

INSERT INTO L_T_EDRSMT_RVII SELECT DISTINCT T1.topactualid, T1.INTERMEDIARYCODE, T1.INTERMEDIARYTYPE, nvl(tt.policypremiumchange, 0) / 100 * (nvl(t1.commissionrate, 0) / 100) * nvl(T3.EXECHANGERATE, 100) AS COMMISSIONAOMUNT -- SUM(NVL(T1.COMMISSIONAOMUNT, 0) / 100 * nvl(T3.EXECHANGERATE, 100)) AS COMMISSIONAOMUNT FROM tcsa.ROLE_V_INTERMEDIARYINFO T1 INNER JOIN tcsa.role_v_premiuminvolved tt ON t1.topactualid = tt.topactualid INNER JOIN circ_audit.L_T_EDRSMT_01 LT ON LT.topactualid = T1.topactualid LEFT OUTER JOIN tcsa.Uccexchange T3 ON T1.CURRENCYCODE = T3.EXCHANGECURRENCY AND TO_CHAR(t3.issuancedate, 'YYYYMMDD') = TO_CHAR(LT.T_INSRNC_BGN_TM, 'YYYYMM') || '01' AND T3.basecurrency = '$$100001000001' 執行計划: Plan hash value: 343405098 -------------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Inst |IN-OUT| -------------------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 26 | 156K| | 6378 (1)| 00:01:17 | | | | 1 | HASH UNIQUE | | 26 | 156K| | 6378 (1)| 00:01:17 | | | |* 2 | HASH JOIN OUTER | | 26 | 156K| | 6377 (1)| 00:01:17 | | | | 3 | VIEW | | 1 | 6058 | | 5598 (1)| 00:01:08 | | | |* 4 | HASH JOIN | | 1 | 6123 | | 5598 (1)| 00:01:08 | | | | 5 | NESTED LOOPS | | 1 | 6097 | | 5596 (1)| 00:01:08 | | | | 6 | MERGE JOIN CARTESIAN | | 1 | 6071 | | 5593 (1)| 00:01:08 | | | | 7 | NESTED LOOPS | | 1 | 6058 | | 1 (100)| 00:00:01 | | | | 8 | VIEW | ROLE_V_INTERMEDIARYINFO | 100 | 589K| | 1 (100)| 00:00:01 | | | | 9 | REMOTE | | | | | | | CLONE | R->S | | 10 | TABLE ACCESS BY INDEX ROWID| L_T_EDRSMT_01 | 1 | 26 | | 0 (0)| 00:00:01 | | | |* 11 | INDEX RANGE SCAN | IDX_L_T_EDRSMT_01_01 | 1 | | | 0 (0)| 00:00:01 | | | | 12 | BUFFER SORT | | 370K| 4702K| | 5593 (1)| 00:01:08 | | | | 13 | REMOTE | POLICY | 370K| 4702K| | 5592 (1)| 00:01:08 | CLONE | R->S | | 14 | REMOTE | PREMIUMINVOLVED | 1 | 26 | | 3 (0)| 00:00:01 | CLONE | R->S | | 15 | VIEW | | 100 | 2600 | | 1 (100)| 00:00:01 | | | | 16 | REMOTE | | | | | | | CLONE | R->S | |* 17 | VIEW | | 31006 | 3512K| | 778 (1)| 00:00:10 | | | |* 18 | WINDOW SORT PUSHED RANK | | 31006 | 2815K| 6632K| 778 (1)| 00:00:10 | | | | 19 | REMOTE | UCCEXCHANGE | 31006 | 2815K| | 111 (1)| 00:00:02 | CLONE | R->S | -------------------------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access(TO_CHAR("ISSUANCEDATE"(+),'YYYYMMDD')=TO_CHAR(INTERNAL_FUNCTION("LT"."T_INSRNC_BGN_TM"),'YYYYMM')||'01' AND "T1"."CURRENCYCODE"="EXCHANGECURRENCY"(+)) 4 - access("B"."ACTUALID"="A"."ROLEID") 11 - access("LT"."TOPACTUALID"="T1"."TOPACTUALID") 17 - filter("RN"(+)=1) 18 - filter(ROW_NUMBER() OVER ( PARTITION BY "T_RMB"."BASECURRENCY","T_RMB"."EXCHANGECURRENCY",TO_DATE(TO_CHAR(INTERNAL_FUNC TION("T_RMB"."ISSUANCEDATE"),'yyyymmdd'),'yyyymmdd') ORDER BY "STATUS")<=1)

觀察執行計划,發現id=6處關鍵字為MERGE JOIN CARTESIAN,也就是笛卡爾積

笛卡兒積一般發生在: 兩個表關聯沒有連接條件的時候就會產生笛卡爾笛卡兒積,這種表連接方式就叫笛卡爾笛卡兒連接。
笛卡爾連接會返回兩個表的乘積。A有100行數據,B有14行數據,兩個表進行笛卡爾連接之后會返回1400行數據

那在這個執行計划中,為什么優化器會選擇笛卡爾積連接呢?

因為Id=7返回的Rows被優化器錯誤的估算為1行,優化器認為1行的表與任意大小的表進行笛卡爾關聯,數據也不會翻翻,優化器認為這是安全的。所以這里優化器選擇了笛卡爾連接。

ID = 7 為NEST LOOPS 內連接嵌套循環關聯方式,所關聯的兩張表為ROLE_V_INTERMEDIARYINFOL_T_EDRSMT_01

優化器錯誤認為L_T_EDRSMT_011條數據,兩表關聯,進行內連接,數據量肯定是返回1行。 而實際上,我們在數據庫中查詢此表,發現數據量有16549行數據
這里寫圖片描述
所以說此處執行計划是錯誤的

但是為什么,優化器會認為L_T_EDRSMT_01為1條數據呢?

因為數據庫統計信息收集的不准。但是為什么收集的不准?

因為運行此存儲過程SP_AUDIT_T_EDRSMT之前,恰好這張表L_T_EDRSMT_01的數據為0。每天晚上10點數據庫內部定時,收集統計信息時,也就把表的的統計信息收集為0了。

優化方法

重新收集統計信息

BEGIN
  DBMS_STATS.gather_schema_stats(ownname          => 'CIRC_AUDIT',
                                estimate_percent => 30,
                                method_opt       => 'for all columns size repeat',
                                no_invalidate    => FALSE,
                                degree           => 4,
                                granularity      => 'ALL',     
                                cascade          => TRUE);
END;
/

收集完成后,該SQL能在10分鍾內運行完成。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM