ORACLE定期清理INACTIVE会话

本文介绍如何在Oracle数据库中识别并清理长时间处于非活动状态(INACTIVE)的会话,以避免因会话数过多而导致资源消耗过大。文章提供SQL查询示例及存储过程,用于定期清理指定程序产生的空闲会话。

 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举例说明。

clip_image002

   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

作者: 潇湘隐者

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接.
### 终止 Oracle 数据库Inactive 状态的会话方法 在 Oracle 数据库中,Inactive 状态的会话通常是指那些已经完成操作但尚未释放资源的会话。虽然这些会话数据库本身的性能影响较小,但如果数量过多或者长期未释放,仍可能引发资源耗尽等问题。因此,有时需要主动终止这些会话。 #### 1. 查询 Inactive 状态的会话 为了定位需要终止的 Inactive 状态会话,可以使用以下查询语句获取相关信息: ```sql SELECT sid, serial#, username, status, last_call_et FROM v$session WHERE status = 'INACTIVE' AND last_call_et >= 1800 -- 过滤超过30分钟无活动的会话 AND username IS NOT NULL; -- 排除系统内部会话 ``` 此查询将返回所有满足条件的 Inactive 状态会话的信息,其中 `last_call_et` 表示最后一次调用的时间间隔(秒),可以根据实际需求调整过滤条件[^1]。 #### 2. 单独终止某个 Inactive 会话 如果只需要手动终止个别 Inactive 会话,可以直接使用以下命令: ```sql ALTER SYSTEM DISCONNECT SESSION '<sid>,<serial#>' IMMEDIATE; ``` 例如,假设查询结果显示某一会话的 `SID=123` 和 `SERIAL#=456`,则可执行如下命令: ```sql ALTER SYSTEM DISCONNECT SESSION '123,456' IMMEDIATE; ``` 相比传统的 `KILL SESSION` 命令,`DISCONNECT SESSION` 更适合用于快速断开会话连接而不必等待其完全退出[^2]。 #### 3. 批量终止多个 Inactive 会话 对于大量存在的 Inactive 会话,建议采用自动化方式批量处理。可以通过创建存储过程实现这一功能。以下是一个示例存储过程定义: ```plsql CREATE OR REPLACE PROCEDURE KILL_INACTIVE_SESSIONS AS BEGIN FOR REC IN ( SELECT sid, serial# FROM v$session WHERE status = 'INACTIVE' AND last_call_et >= 1800 -- 设置阈值为30分钟 AND username IS NOT NULL -- 排除系统内部会话 ) LOOP EXECUTE IMMEDIATE 'ALTER SYSTEM DISCONNECT SESSION ''' || TO_CHAR(REC.sid) || ',' || TO_CHAR(REC.serial#) || ''' IMMEDIATE'; END LOOP; END; / ``` 执行该存储过程即可一次性清理符合条件的所有 Inactive 会话[^4]。 #### 4. 配置自动清理机制 为了避免频繁人工干预,还可以配置定期任务来自动检测并清理 Idle 的 Inactive 会话。利用 Oracle 提供的任务调度器 DBMS_SCHEDULER 创建定时作业: ```plsql BEGIN DBMS_SCHEDULER.CREATE_JOB ( job_name => 'AUTO_KILL_INACTIVE_SESSIONS', job_type => 'PLSQL_BLOCK', job_action => 'BEGIN KILL_INACTIVE_SESSIONS; END;', start_date => SYSTIMESTAMP, repeat_interval => 'FREQ=DAILY; INTERVAL=1', -- 每天运行一次 enabled => TRUE); END; / ``` --- ### 注意事项 - **权限要求**: 上述操作均需由具有管理员权限的用户执行。 - **业务影响评估**: 在生产环境中随意终止会话可能会干扰正常业务流程,尤其是某些应用程序依赖于保持持久连接的情况下。务必谨慎行事,并提前通知相关人员。 - **优化替代方案**: 若发现频繁出现大量 Inactive 会话,应考虑从根源解决问题,例如改进客户端程序设计、合理设置连接池大小或启用空闲超时机制等。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值