1. 程式人生 > >檢視Oracle中儲存過程長時間被卡住的原因

檢視Oracle中儲存過程長時間被卡住的原因

1:查V$DB_OBJECT_CACHE

SELECT * FROM V$DB_OBJECT_CACHE WHERE name='CUX_OE_ORDER_RPT_PKG' AND LOCKS!='0';

注意:CUX_OE_ORDER_RPT_PKG 為儲存過程的名稱。

發現 locks=2

2:按物件查出sid的值

select /*+ rule*/  SID from V$ACCESS WHERE object='CUX_OE_ORDER_RPT_PKG';

注意:CUX_OE_ORDER_RPT_PKG 為儲存過程的名稱。

3:查sid,serial#

SELECT SID,SERIAL#,PADDR FROM V$SESSION WHERE SID='剛才查到的SID';

4、根據會話id(sid),此會話的等待事件:

[sql]  view plain copy
  1. select * from v$session where sid=***;  

event欄位即為等待事件。查詢後我們發現這個會話等待事件為SQL*Net message from dblink;在檢視會話的logon_time為兩天前。這個時間遠超過我們估計時間。

5、根據會話id檢視此會話正在執行的sql語句

[sql]
 view plain copy
  1. select sql_text from v$sqlarea where address= (select   sql_address  from v$session where sid=***);  

查詢後發現正在執行的sql語句為通過dblink到遠端資料庫上A表查詢資料,插入到B表。

6、連線遠端資料庫,查詢當前被鎖的物件

[sql]
 view plain copy
  1. select * from v$locked_object lo ,   
  2. all_objects  ao    where lo.OBJECT_ID= ao.object_id ;  

檢視後發現遠端資料庫中並沒有涉及到A、B表被鎖

7、檢視遠端資料的會話:

[sql]  view plain copy
  1. select * from v$session where terminal like '%機器名%'  and program='Oracle.exe'  

使用dblink連線遠端資料庫,在遠端資料庫上的會話的program應該是是oracle.exe

查詢後發現,兩個遠端庫有時候根本沒有相關會話,有時候可能有相關會話,但其等待事件是 SQL*Net message from client 遠端庫在等待本地Oracle給他發請求。

 

本地庫等dblink遠端庫,遠端庫等待client訊息。看來這個儲存過程是不可能執行完了。

具體什麼原因造成了,還不清楚。

 

這裡給出的處理方法就是殺死會話

http://blog.csdn.net/fupei/article/details/7325190

具體步驟可參考上面的文章

 

一些專案中使用了job定期執行sql語句。如果要執行的sql語句是基於dblink對遠端資料庫的訪問,那麼有時候就會出現該sql語句長時間執行一直不結束的情況。並且這時在遠端資料庫上並沒有鎖導致該sql語句等待(這可能是由於網路問題觸發的oracle的一個bug吧,遠端資料庫與本地資料之間有防火牆時比較容易出現這個現象)。

下面總結了如何判斷該job是否長時間執行沒結束,並說明了處理步驟。


1)、觀察job情況。
   system使用者下執行語句select * from  dba_jobs;找到有問題的job,記錄下該job在查詢結果中job列的取值,該取值稱為job號。
   broken欄位為N,且this_date欄位的時間比當前時間減去執行週期要晚(根據interval欄位判斷),則job是正常的。如果this_date欄位沒有值,一般認為job當前沒有在執行。
   如果broken欄位N,並且this_date時間不對(例如是幾個小時以前,甚至幾天以前),則說明該job某一次週期一直沒有執行完。
   如果出現這種現象,就說明該job可能出問題了。
     
2)、查詢該job目前正在執行時的會話編號sid
   select * from  dba_jobs_running where job='剛才查到的job號';
   在返回結果中記錄sid列的取值

3)、檢視該會話
   select * from v$session where sid='剛才查到的sid'
   記錄下返回結果的 serial#列(會話序列號),paddr列(執行緒地址)

4)、 取得會話的執行緒號
select spid from  v$process  where   addr='剛才查到的執行緒地址' ;
記錄下列spid,稱為執行緒號

5)、使用oracle命令殺會話
     alter system kill session '會話編號sid,會話序列號serial#';

6)、檢視是否成功殺掉該會話(方法與步驟一相同,多執行幾次select * from  dba_jobs;觀察結果)

7)、如果沒有殺掉會話,就是用作業系統命令殺執行緒(或程序)
    這裡給出windows下殺oracle會話佔用的執行緒的方法  
    登入到資料庫所在的作業系統中,開啟windows命令列,鍵入命令:  orakill 資料庫sid  剛才查到的執行緒號spid

   例如 orakill orcl  12345

 

 

這裡給出一個自動清理問題job的儲存過程,由於是儲存過程,只能使用alter system kill 來殺會話,有時候會話只被標記為killed,並不能真正結束,job也無法啟動下一個週期。

 
CREATE OR REPLACE PROCEDURE SYS.PRO_KILL_JOB AS
  /*清理job567 568 569 長期執行不結束的情況*/
  /*30分鐘超時*/
 CURSOR   MYCUR     IS
   select     ' ALTER SYSTEM KILL SESSION '''||s.sid ||','||  s.SERIAL#||''' immediate '   AS SQL_KILL   , J.JOB
      from dba_jobs_running j,v$session s  
      where   j.sid=s.sid and
       this_date <(sysdate-30/24/60) and
       s.sid is not null and s.serial# is not null
       and
       ( j.job= 567
         or j.job=568
         or j.job=569  ) ;
 
  V_SQL_KILL  VARCHAR2(500);
  V_JOB NUMBER ;
 
 
BEGIN
   OPEN MYCUR;
  LOOP
    FETCH MYCUR
      INTO V_SQL_KILL , V_JOB;
    EXIT WHEN MYCUR%NOTFOUND;
    dbms_output.put_line(v_sql_kill);
    execute immediate  v_sql_kill ;
    COMMUNICATION.SP_DB_LOG('PRO_KILL_JOB', 1, NULL, V_JOB||' IS KILLED');
    COMMIT;
  END LOOP;
  CLOSE MYCUR;
EXCEPTION
  WHEN OTHERS THEN
    COMMUNICATION.SP_DB_LOG('PRO_KILL_JOB', 1, SQLCODE, SQLERRM);
    COMMIT;
END PRO_KILL_JOB;