1. 程式人生 > >記一次Oracle資料故障排除過程

記一次Oracle資料故障排除過程

前天在Oracle生產環境中,自己的儲存過程執行時間超過1小時,懷疑是其他job執行時間過長推遲了自己job執行時間,遂重新跑job,發現同測試環境的確不同,運行了25分鐘。

之後準備在測試環境中製造同數量級的資料進行分析,寫了大概如下的儲存過程,

create or replace PROCEDURE PERFORMANCE_TEST AS 

v_date date;
v_start_date date;
v_end_date date;
v_start_date_str varchar2(10) := '2017-01-31';
v_end_date_str varchar2(10) :=
'2017-07-31'; v_date_str varchar2(10); BEGIN v_start_date := to_date(v_start_date_str, 'yyyy-mm-dd'); v_end_date := to_date(v_end_date_str, 'yyyy-mm-dd'); v_date := v_start_date; while v_date < v_end_date loop v_date_str := to_char(v_date, 'yyyy-mm-dd'); insert into datacore.df_customer_static_report ( data_date, cty_code, party_id, party_name, ho_domicile_cty, rm_code, rm_name, business_division )(
select v_date_str, cty_code, party_id, party_name, ho_domicile_cty, rm_code, rm_name, business_division from datacore.df_customer_static_report where data_date = v_end_date_str ); commit; end loop; END PERFORMANCE_TEST;

犯了個致命錯誤,丟了v_date := v_date + 1; 儲存過程陷入無限迴圈!在過了1個多小時後,意識到不對勁,遂查詢了資料量,發現2017-01-31的資料量竟然達到了千萬級。。。趕緊停止執行找原因,才發現無限迴圈插入資料。

剩下就是怎麼刪掉這些資料,畢竟千萬級的資料佔據儲存空間太大了。簡單的刪除肯定不起作用,遂嘗試分批刪除,先試著刪除1w條,結果執行很長時間後還是沒有結束。這個時候,感覺之前那個無限迴圈應該還沒有結束,在後臺還在執行。因為其他事情搗亂,沒來得及修正這個問題。第二天來,再次查詢,發現數據量達到了快5千萬條,欲哭無淚啊!趕緊刪資料,分批次,1百萬條的刪,(這次加上累加條件了)

create or replace PROCEDURE DELETE_TEMP AS 

v_number number := 1;
v_number_end number := 50;

BEGIN

  while v_number <= v_number_end loop
    delete from datacore.df_customer_static_report nologging
    where data_date = '2017-01-31'
    and rownum < 1000000;
    commit;
    v_number := v_number + 1;
    dbms_output.put_line(v_number || ' end');
  end loop;
  
END DELETE_TEMP;

本想用TRUNCATE把所有資料都刪掉,但是我這裡只需要刪掉表中‘2017-01-31’的資料,而且只是把千萬條降低到萬條。查了delete語句的優化,發現加上nologging會更快些(資料不做恢復)。

運行了大概1個小時後,感覺差不多了,遂手動終止了delete的執行。再次查詢,‘2017-01-31’的資料降到9千多條。竊喜~

不過又想起昨天想到的“是否無限迴圈還在後臺執行”?過了10幾分鐘後查詢,發現數據又多了,欲哭無淚。。

怎麼讓這個討厭的無限迴圈終止呢?因為使用的賬號沒有dba許可權,所以想通過更改表的結構,讓包含無限迴圈的儲存過程異常終止。但估計不可行,因為資料庫表一直被佔用了。抱著試一試的想法,執行以下sql,

alter table datacore.df_customer_static_report
drop column rds_spread_code

報錯“資源正忙, 但指定以 NOWAIT 方式獲取資源, 或者超時失效;resource busy and acquire with NOWAIT specified”。

搜尋後,果真有解決方案 - here,但還是需要dba許可權(厚臉皮要吧)。

1. 用dba許可權的使用者檢視資料庫都有哪些鎖

SELECT T2.USERNAME,T2.SID,T2.SERIAL#,T2.LOGON_TIME
FROM V$LOCKED_OBJECT T1,V$SESSION T2
WHERE T1.SESSION_ID=T2.SID ORDER BY T2.LOGON_TIME;

2. 根據sid檢視具體的sql語句,如果sql不重要,可以kill

SELECT SQL_TEXT FROM V$SESSION A,V$SQLTEXT_WITH_NEWLINES B
WHERE DECODE(A.SQL_HASH_VALUE, 0, PREV_HASH_VALUE, SQL_HASH_VALUE)=B.HASH_VALUE
AND A.SID=&SID ORDER BY PIECE;

3. kill該事務

ALTER SYSTEM KILL SESSION '590,20839';

4. 再次檢視資料庫鎖,發現鎖消失。再次查詢表資料,不再增加。

教訓:以後寫儲存過程中的迴圈,千萬注意條件的累加!