ORACLE定期清理INACTIVE会话

本文介绍如何在Oracle数据库中清理长时间处于非活跃状态的会话,以避免资源浪费及会话数量超出限制的问题。文章提供了SQL查询示例用于识别符合条件的会话,并通过存储过程实现了定期自动清理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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分钟运行一次,清理哪些满足条件的空闲会话。

<think>我们正在处理一个关于Oracle数据库的问题。用户提到在手动停止loader(可能是数据加载工具)后,Oracle会话未终止,需要解决方案。根据Oracle数据库的知识,当一个会话被异常终止(例如,强制停止客户端程序),数据库会话可能不会立即释放,而是会变成“INACTIVE”状态,并可能在一段时间后由PMON(进程监视器)清理。但有时会话可能长时间保持,需要手动干预。解决方案通常包括:1.识别未终止的会话:通过查询数据字典视图(如v$session)找到对应的会话。2.手动终止会话:使用ALTERSYSTEMKILLSESSION命令终止会话。需要注意的是,终止会话可能会影响事务的一致性,因此应确保该会话确实没有进行重要操作。另外,根据引用[1]中提到的OracleRAC特性,在RAC环境中,终止会话可能需要额外的考虑,比如指定实例ID(INST_ID)。步骤:1.查询会话:使用SQL查询找到需要终止的会话的SID和SERIAL#(以及INST_ID,如果是RAC)。2.终止会话:使用ALTERSYSTEMKILLSESSION命令。如果会话无法立即终止(例如,它正在回滚事务),可以加上IMMEDIATE选项强制终止,但这可能会导致事务回滚需要时间,且无法中断回滚。如果会话状态为“KILLED”但长时间不释放,可能需要重启实例,但这通常是最后的手段。详细步骤如下:###步骤1:查询会话首先,我们需要找到与loader相关的会话。可以通过查询`v$session`视图,根据机器名、程序名或用户名等条件定位。例如:```sqlSELECTsid,serial#,username,status,machine,program,inst_idFROMgv$sessionWHEREprogramLIKE'%loader%'ORmachine='loader_machine_name';```注意:在RAC环境中,使用`gv$session`(全局视图)并注意`inst_id`。在单实例中,使用`v$session`。###步骤2:终止会话获取到SID和SERIAL#后,使用以下命令终止会话:```sqlALTERSYSTEMKILLSESSION'sid,serial#';--单实例```在RAC环境中,如果查询的是`gv$session`,则还需要指定`inst_id`:```sqlALTERSYSTEMKILLSESSION'sid,serial#,@inst_id';```或者使用:```sqlALTERSYSTEMKILLSESSION'sid,serial#'IMMEDIATE;```使用`IMMEDIATE`选项会立即终止会话,而不等待当前操作完成(但回滚操作仍会继续)。###步骤3:检查会话状态终止后,再次查询`v$session`,确认会话状态变为`KILLED`,然后等待PMON进程清理。如果长时间不释放,可能需要进一步操作。###特殊情况处理如果会话状态为“KILLED”但长时间存在,可能是因为事务回滚量很大。此时,可以尝试在操作系统级别杀掉对应的服务器进程(如果知道进程ID):1.从`v$session`中找到对应的`paddr`(进程地址),然后通过`v$process`视图找到操作系统进程ID(SPID)。2.在操作系统级别(Unix/Linux)使用`kill-9spid`命令。注意:在操作系统级别杀掉进程应谨慎,可能导致实例不稳定,尤其是系统进程。###预防措施为了避免这种情况,可以在loader程序中增加正常的断开连接处理,确保在停止loader时,它会主动关闭数据库会话。此外,可以设置数据库的PROFILE,限制会话的空闲时间,例如:```sqlALTERPROFILEdefaultLIMITIDLE_TIME30;--空闲30分钟后自动断开```但请注意,这可能会影响其他会话。根据引用[2],Oracle数据库有自动维护任务和顾问工具,但这个问题更多是会话管理,所以手动干预是主要方法。###代码示例以下是一个可能的脚本,用于查找并终止loader会话(在单实例环境中):```sql--查询loader会话SELECTsid,serial#,username,statusFROMv$sessionWHEREprogramLIKE'%loader%';--假设找到的sid为123,serial#为456ALTERSYSTEMKILLSESSION'123,456'IMMEDIATE;```在RAC环境中:```sqlSELECTinst_id,sid,serial#,username,statusFROMgv$sessionWHEREprogramLIKE'%loader%';--假设inst_id=1,sid=123,serial#=456ALTERSYSTEMKILLSESSION'123,456,@1'IMMEDIATE;```###注意事项-终止会话会导致该会话当前的事务回滚,如果事务很大,回滚可能需要时间。-在RAC环境中,确保在正确的实例上终止会话。-如果会话正在执行重要操作(如数据加载),终止可能导致数据不一致,需评估影响。如果上述方法无法终止会话,并且会话状态一直为“KILLED”,可能需要重启数据库实例,但这通常不是推荐的做法。另外,引用[3]提到了ASM磁盘组兼容性属性,但这个问题与ASM无关,因此我们不需要考虑。综上所述,解决方案的核心是手动终止会话。现在,生成相关问题:1.如何查询Oracle中当前的活动会话?2.在OracleRAC环境中终止会话有什么不同?3.如何设置Oracle会话的空闲超时时间?4.如果Oracle会话状态为“KILLED”但长时间不释放,应该怎么办?5.如何在操作系统级别终止Oracle会话对应的进程?</think>### 手动停止 Loader 后 Oracle 会话未终止的解决方案 当手动终止数据加载工具(如 SQL*Loader、外部表加载程序等)后,Oracle 会话未释放的原因通常是客户端进程被强制结束,但数据库服务端会话状态未同步更新。以下是逐步解决方案: --- #### **1. 定位残留会话** 首先查询活跃会话,找出与 Loader 相关的残留会话: ```sql SELECT s.sid, s.serial#, s.username, s.status, s.machine, s.program, s.sql_id FROM v$session s WHERE s.program LIKE '%loader%' -- 替换为实际 Loader 程序名(如:'%sqlldr%') OR s.machine = '<客户端机器名>' AND s.status = 'INACTIVE'; -- 重点关注 INACTIVE/KILLED 状态会话 ``` - **关键字段**:记录 `SID` 和 `SERIAL#`(终止会话必需) - **特殊场景(RAC)**:使用 `gv$session` 并添加 `INST_ID` 列 --- #### **2. 手动终止会话** 使用获取的 `SID` 和 `SERIAL#` 终止会话: ```sql ALTER SYSTEM KILL SESSION '<sid>,<serial#>' IMMEDIATE; -- 单实例 ``` - **RAC 环境**: ```sql ALTER SYSTEM KILL SESSION '<sid>,<serial#>,@<inst_id>' IMMEDIATE; -- 指定实例 ID ``` - **`IMMEDIATE` 作用**: 强制释放会话资源,但事务回滚可能仍在后台进行[^1]。 > ⚠️ **注意**:若会话状态变为 `KILLED` 但长时间不释放,说明存在未完成事务(如回滚操作),需进入步骤 3。 --- #### **3. 处理顽固会话(状态为 KILLED)** 当 `ALTER SYSTEM KILL SESSION` 无效时: 1. **查找操作系统进程 ID (SPID)**: ```sql SELECT p.spid, s.sid, s.serial# FROM v$session s JOIN v$process p ON s.paddr = p.addr WHERE s.sid = <目标SID>; ``` 2. **在操作系统层终止进程**: ```bash # Linux/Unix kill -9 <SPID> # Windows orakill <ORACLE_SID> <SPID> ``` --- #### **4. 预防措施** - **设置会话超时**: ```sql ALTER PROFILE DEFAULT LIMIT IDLE_TIME 30; -- 空闲30分钟自动断开 ``` - **程序优化**: 在 Loader 脚本中添加异常处理,确保退出时执行 `COMMIT/ROLLBACK` + `DISCONNECT`。 - **连接池配置**: 启用 Oracle RAC 的快速连接故障通知功能,避免 TCP 超时等待[^1]。 --- #### **5. 验证会话释放** 终止操作后确认结果: ```sql SELECT sid, serial# FROM v$session WHERE sid = <目标SID>; -- 无返回即成功 ``` > 💡 **高级场景**: > 若会话持续残留,检查 `v$transaction` 是否存在未提交事务,或使用 Oracle 性能顾问分析会话阻塞链[^2]。 --- ### 相关问题 1. 如何监控 Oracle 中的长时间空闲会话? 2. 在 RAC 环境中终止会话与单实例有何区别? 3. Oracle 的 `ALTER SYSTEM KILL SESSION` 和操作系统 `kill` 命令有何风险差异? 4. 如何配置 Oracle 连接池以快速检测失效会话? 5. 哪些数据字典视图可查看被锁定的会话信息? [^1]: Oracle RAC 提供与 Oracle 客户端和连接池的内置集成。有了这一功能,应用程序可以立即从终止该连接的池得到任何故障通知。应用程序可以避免等待 TCP 超时,并可以立即采取适当的恢复操作。 [^2]: 本节介绍了包括各种顾问工具在内的 Oracle 数据库性能调优的各个重要方面。Oracle 数据库顾问就如何解决关键数据库管理难题提供了具体的建议,并涉及广泛的领域,包括空间、性能、撤消管理等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值