在週末時一位朋友諮詢有沒有自動殺會話的腳本,今天就和大家分享一下自己常用的兩個殺會話的腳本。殺會話這個工作本身來説非常的簡單,但是在日常工作中怎麼能把殺會話的效率做到極致是我們衡量的一個標準,因為在系統緊急故障時,我們需要通過殺會話來快速恢復系統,如果殺會話的速度跟不上,那整個系統就直接掛了。記得曾經在駐場時就出現過兩次類似的情況:一次是國慶值班時,跟值班的人坐在一起,旁邊值班工程師給甲方打電話,説數據庫會話有異常,要求DBA趕快速度,我聽到後立馬登錄數據庫,執行一次we.sql腳本,定位到有異常的會話,立馬執行kill session的腳本,整個過程不到一分鐘,等我殺完會話後,接到甲方人員的電話,我立馬回覆,目前數據庫已經正常。另外還一次是某系統運行在VMware平台,每次收到監控告警到整個Hang住時間不會超過2分鐘,所以後續收到監控告警後,上去裏面就是一頓咔咔的殺,殺兩遍會話後數據庫最少不會Hang,保證了數據庫的連續性。很多數據庫運維的工作其實不在難度,而在於熟練度和效率,想要提升熟練度和效率,腳本是我們最好的方式。

腳本的獲取

關注公眾號,後台回覆“腳本”二字,已經關注並回復過的就直接下載即可。

kill_sess_80.sql

這個腳本主要的目的當數據庫Process數達到參數Process配置的80%的時候,就會根據last_call_etl來降序排列非活動的會話,每次殺排名前9的會話,直到當前進程數低於80%。這個腳本的目的主要是用於醫療、企業、能源等多個行業中。

關鍵代碼如下:

SELECT ROUND (v1 / v2, 2)
      INTO v_count
      FROM (SELECT COUNT (*) v1 FROM v$session) a,
           (SELECT VALUE v2
              FROM v$parameter
             WHERE name = 'processes') b;

    WHILE v_count > 0.8
    LOOP
        FOR cur_session
            IN (SELECT *
                  FROM (  SELECT sid, serial#, last_call_et
                            FROM v$session s
                           WHERE     s.username NOT IN ('SYSTEM',
                                                        'SYS',
                                                        'SYSMAN',
                                                        'DBSNMP')
                                 AND S.USERNAME IS NOT NULL
                                 AND s.status = 'INACTIVE'
                                 AND s.machine <>
                                         SYS_CONTEXT ('userenv', 'host')
                        ORDER BY 3 DESC)
                 WHERE ROWNUM < 10)
        LOOP

使用方法

sqlplus '/ as sysdba'登錄數據庫

@kill_sess_80.sql

kill_sess_by_where.sql

在殺會話時,我們常常會改變各種殺會話的條件,比如上面的根據last_call_et也有可能根據program或者具體的sid來殺會話,所以感覺手裏面永遠都缺少一個殺會話的腳本,所以後面引用國外大佬的snapper.sql的腳本中的一段代碼來編寫了一個通用的殺會話的腳本,可以自定很多條件,也可以自定義select查詢條件。

目前腳本支持接下面的這種條件:

con_id=   
sid=      
audsid=   
user=     
username= 
machine=  
program=  
service=  
module=   
action=   
osuser=   
client_id=
spid=     
ospid=    
pid=      
qcsid=    
qc=       
all       
fg        
select    
(

其中的核心代碼如下:

過濾條件部分:
    -- compute sid_filter
    case
        when trim(lower('&ssid_begin')) like 'con_id=%'    then lv_sid_filter   := 's.con_id in ('||get_filter('&ssid_begin')||')';
        when trim(lower('&ssid_begin')) like 'sid=%'       then lv_sid_filter   := 's.sid in ('   ||get_filter('&ssid_begin')||')'; 
        when trim(lower('&ssid_begin')) like 'audsid=%'    then lv_sid_filter   := 's.audsid in ('||get_filter('&ssid_begin')||')'; 
        when trim(lower('&ssid_begin')) like 'user=%'      then lv_sid_filter   := 'lower(username) like '''         ||get_filter('&ssid_begin')||'''';
        when trim(lower('&ssid_begin')) like 'username=%'  then lv_sid_filter   := 'lower(username) like '''         ||get_filter('&ssid_begin')||'''';
        when trim(lower('&ssid_begin')) like 'machine=%'   then lv_sid_filter   := 'lower(machine) like '''          ||get_filter('&ssid_begin')||'''';
        when trim(lower('&ssid_begin')) like 'program=%'   then lv_sid_filter   := 'lower(program) like '''          ||get_filter('&ssid_begin')||'''';
        when trim(lower('&ssid_begin')) like 'service=%'   then lv_sid_filter   := 'lower(service_name) like '''     ||get_filter('&ssid_begin')||'''';
        when trim(lower('&ssid_begin')) like 'module=%'    then lv_sid_filter   := 'lower(module) like '''           ||get_filter('&ssid_begin')||'''';
        when trim(lower('&ssid_begin')) like 'action=%'    then lv_sid_filter   := 'lower(action) like '''           ||get_filter('&ssid_begin')||'''';
        when trim(lower('&ssid_begin')) like 'osuser=%'    then lv_sid_filter   := 'lower(osuser) like '''           ||get_filter('&ssid_begin')||'''';
        when trim(lower('&ssid_begin')) like 'client_id=%' then lv_sid_filter   := 'lower(client_identifier) like '''||get_filter('&ssid_begin')||'''';
        when trim(lower('&ssid_begin')) like 'spid=%'      then lv_sid_filter   := '(s.inst_id,s.paddr) in (select /*+ UNNEST */ inst_id,addr from gv$process where spid in ('||get_filter('&ssid_begin')||'))';
        when trim(lower('&ssid_begin')) like 'ospid=%'     then lv_sid_filter   := '(s.inst_id,s.paddr) in (select /*+ UNNEST */ inst_id,addr from gv$process where spid in ('||get_filter('&ssid_begin')||'))';
        when trim(lower('&ssid_begin')) like 'pid=%'       then lv_sid_filter   := '(s.inst_id,s.paddr) in (select /*+ UNNEST */ inst_id,addr from gv$process where spid in ('||get_filter('&ssid_begin')||'))';
        when trim(lower('&ssid_begin')) like 'qcsid=%'     then lv_sid_filter   := '(s.inst_id,s.sid)   in (select /*+ NO_UNNEST */ inst_id,sid from gv$px_session where qcsid in ('||get_filter('&ssid_begin')||'))';
        when trim(lower('&ssid_begin')) like 'qc=%'        then lv_sid_filter   := '(s.inst_id,s.sid)   in (select /*+ NO_UNNEST */ inst_id,sid from gv$px_session where qcsid in ('||get_filter('&ssid_begin')||'))';
        when trim(lower('&ssid_begin')) like 'all%'        then lv_sid_filter   := '1=1 and type=''USER''';
        when trim(lower('&ssid_begin')) like 'fg%'         then lv_sid_filter   := 'type=''USER''';
        when trim(lower('&ssid_begin')) like 'select%'     then lv_sid_filter   := q'{(s.inst_id,s.sid) in (&snapper_sid)}';
        when trim(lower('&ssid_begin')) like '(%'          then lv_inst_filter  := '/* inst_filter2 */ 1=1'; lv_sid_filter := q'{(s.inst_id,s.sid) in (&snapper_sid)}'; 
        else                                                    lv_sid_filter   := '/* sid_filter_else_cond */ s.sid in ('||get_filter('&ssid_begin')||')'; 
    end case;
    
    

      FOR cur_session    IN
            (SELECT * FROM gv$session s
                WHERE    1=1
                and &sid_filter
              )
    LOOP
        BEGIN

        if (cur_session.inst_id <> userenv('instance')) then
          v_sid:=cur_session.sid||','||cur_session.serial#||',@'||cur_session.inst_id;
        else
          v_sid:=cur_session.sid||','||cur_session.serial#;
        end if;

        v_sql :='alter system kill session '|| CHR (39)||v_sid|| CHR (39)|| ' immediate';

殺會話的命令:
        Execsql (cur_session.program||':'||cur_session.username||':'||cur_session.inst_id||':'||cur_session.sid||':'||cur_session.status,v_sql, debug);
        EXCEPTION
            WHEN OTHERS THEN
              DBMS_OUTPUT.put_line('Find Error on :'||SQLERRM||':'||cur_session.sid);
        END;

使用方法

@kill_sess_by_where.sql <篩選條件> [調試模式]

參數説明:

  • &1:篩選條件(必需),可以參考上面內容
  • &2:調試模式,1=僅顯示KILL命令,0=實際執行KILL操作(可選,默認1)

常用使用案例

1. 按用户名終止會話

@kill_sess_by_where.sql "user=SCOTT" 0
-- 終止所有用户名為SCOTT的會話

2. 按機器名終止會話

@kill_sess_by_where.sql "machine=server01" 0
-- 終止來自server01機器的所有會話

3. 按程序名終止會話

@kill_sess_by_where.sql "program=sqlplus" 0
-- 終止所有sqlplus程序的會話

4. 按模塊名終止會話

@kill_sess_by_where.sql "module=MyApp" 0
-- 終止模塊名為MyApp的所有會話

5. 按服務名終止會話

@kill_sess_by_where.sql "service=MYDB" 0
-- 終止連接到MYDB服務的所有會話

6. 按SID終止特定會話

@kill_sess_by_where.sql "sid=123" 0
-- 終止SID為123的會話

還有更多的使用方法大家可以參考一下。

------------------作者介紹-----------------------

姓名:黃廷忠

現就職:Oracle中國高級服務團隊

曾就職:OceanBase、雲和恩墨、東方龍馬等