ORACLE數據庫會話有ACTIVE、INACTIVE、KILLED、 CACHED、SNIPED五種狀態。INACTIVE狀態的會話表示此會話處於非活動、空閑、等待狀態。例如PL/SQL Developer連接到數據庫,執行一條SQL語句后,如果不繼續執行SQL語句,那么此會話就處於INACTIVE狀態。一般情況下,少量的INACTVIE會話對數據庫並沒有什么影響,如果由於程序設計等某些原因導致數據庫出現大量的會話長時間處於INACTIVE狀態,那么將會導致大量的系統資源被消耗,造成會話數超過系統session的最大值,出現ORA-00018:maximum number of sessions exceeded錯誤。
有時候需要清理那些長時間處於INACTIVE狀態的會話。人為定期檢查、殺掉這類會話肯定不太現實,要定期清理那些長時間處於INACTIVE的會話,只能通過作業來實現;另外需要注意,Kill掉這些會話需要需要謹慎,稍不注意,就有可能誤殺了一些正常的會話。那么我們該如何定義這類會話呢?下面是我結合業務規則定義的:
1: 會話的Status必須為INACTIVE,如果會話狀態為ACTIVE、KILLED、CACHED、SNIPED狀態,不做考慮。
2: 會話必須已經長時間處於INACTIVE狀態。例如,處於INACTIVE狀態超過了兩小時的會話進程,才考慮Kill。這個視具體業務或需求決定,有可能超過半小時就可以殺掉會話進程。至於如何計算處於INACTIVE會話狀態的時間,這個可以 通過V$SESSION的LAST_CALL_ET字段來判別,需要查詢處於INACTIVE狀態兩小時或以上的會話,就可以通過查詢條件S.LAST_CALL_ET >= 60*60*2實現,當然最好寫成 S.LAST_CALL_ET >= 7200
3: 連接到會話的程序。比如,某個特定的應用程序產生的INACTIVE會話才要清理。例如, Toad工具、PL/SQL Developer工具。關於PROGRAM這個需要根據當前項目的具體情況設置,下面僅僅使用TOAD.EXE、W3WP.EXE舉例說明。
1: SELECT SID, SERIAL#,MODULE, STATUS
2: FROM V$SESSION S
3: WHERE S.USERNAME IS NOT NULL
4: AND UPPER(S.PROGRAM) IN ('TOAD.EXE', 'W3WP.EXE')
5: AND S.LAST_CALL_ET >= 60*60*2
6: AND S.STATUS = 'INACTIVE'
7: ORDER BY SID DESC;
如果是RAC環境,那么最好使用下面SQL語句,使用全局視圖GV$SESSION。
1: SELECT SID, SERIAL#, INST_ID, MODULE,STATUS
2: FROM gv$session S
3: WHERE S.USERNAME IS NOT NULL
4: AND UPPER(S.PROGRAM) IN ('TOAD.EXE', 'W3WP.EXE')
5: AND S.LAST_CALL_ET >= 2 * 60*60
6: AND S.STATUS = 'INACTIVE'
7: ORDER BY INST_ID DESC
接下來創建存儲過程SYS.DB_KILL_IDLE_CLIENTS. 方便調用該功能執行kill inactive 會話。注意:xxx部分用實際業務的PROGRAM來替代。
1:
2: CREATE OR REPLACE PROCEDURE SYS.DB_KILL_IDLE_CLIENTS AUTHID DEFINER AS
3: job_no number;
4: num_of_kills number := 0;
5: BEGIN
6:
7: FOR REC IN
8: (SELECT SID, SERIAL#, INST_ID, MODULE,STATUS
9: FROM gv$session S
10: WHERE S.USERNAME IS NOT NULL
11: AND UPPER(S.PROGRAM) IN ('xxx', 'xxx.EXE')
12: AND S.LAST_CALL_ET >= 2*60*60
13: AND S.STATUS= 'INACTIVE'
14: ORDER BY INST_ID ASC
15: ) LOOP
16: ---------------------------------------------------------------------------
17: -- kill inactive sessions immediately
18: ---------------------------------------------------------------------------
19: DBMS_OUTPUT.PUT('LOCAL SID ' || rec.sid || '(' || rec.module || ')');
20: execute immediate 'alter system kill session ''' || rec.sid || ', ' ||
21: rec.serial# || '''immediate' ;
22:
23: DBMS_OUTPUT.PUT_LINE('. killed locally ' || job_no);
24: num_of_kills := num_of_kills + 1;
25: END LOOP;
26: DBMS_OUTPUT.PUT_LINE ('Number of killed xxxx system sessions: ' || num_of_kills);
27: END DB_KILL_IDLE_CLIENTS;
28: /
另外,由於kill session是直接將session kill掉,有可能出現導致事物回滾的現象,其實我們可以使用disconnect session完成當前事務並終止session。這種方式比alter system kill session跟安全可靠。
1: CREATE OR REPLACE PROCEDURE SYS.DB_KILL_IDLE_CLIENTS AUTHID DEFINER AS
2: job_no number;
3: num_of_kills number := 0;
4: BEGIN
5:
6: FOR REC IN
7: (SELECT SID, SERIAL#, INST_ID, MODULE,STATUS
8: FROM gv$session S
9: WHERE S.USERNAME IS NOT NULL
10: AND UPPER(S.PROGRAM) IN ('xxxx', 'xxxx')
11: AND S.LAST_CALL_ET >= 2*60*60
12: AND S.STATUS<>'KILLED'
13: ORDER BY INST_ID ASC
14: ) LOOP
15: ---------------------------------------------------------------------------
16: -- kill inactive sessions immediately
17: ---------------------------------------------------------------------------
18: DBMS_OUTPUT.PUT('LOCAL SID ' || rec.sid || '(' || rec.module || ')');
19: execute immediate 'alter system disconnect session ''' || rec.sid || ', ' ||
20: rec.serial# || '''immediate' ;
21:
22: DBMS_OUTPUT.PUT_LINE('. killed locally ' || job_no);
23: num_of_kills := num_of_kills + 1;
24: END LOOP;
25: DBMS_OUTPUT.PUT_LINE ('Number of killed system sessions: ' || num_of_kills);
26: END DB_KILL_IDLE_CLIENTS;
27: /
然后,我們可以在作業(JOB)或Schedule里面定期調用該存儲過程,也可以通過后台作業結合shell腳本實現定期清理空閑會話的功能。例如如下所示。
創建killSession.sh腳本,調用該存儲過程SYS.DB_KILL_IDLE_CLIENTS
1: #!/bin/bash
2:
3:
4:
5: logfile=/home/oracle/cron/session/log/killSession.log
6:
7: echo " " >> $logfile 2>&1
8: echo "START ----`date`" >> $logfile 2>&1
9: sqlplus /nolog <<STATS
10: connect / as sysdba
11: exec sys.db_kill_idle_clients;
12: exit;
13: STATS
14:
15: echo "END ------`date`" >> $logfile 2>&1
在crontab里面配置后台作業,每隔15分鍾運行一次,清理哪些滿足條件的空閑會話。
0,15,30,45 * * * * /home/oracle/cron/session/bin/killSession.sh >/dev/null 2>&1