应查询v$session中last_call_et>3600、username非sys/system、type='user'、state='waiting'且event like 'sql*net%'的会话,再结合program、machine和业务上下文判断是否真正空闲。
oracle 不会自动断开空闲会话,必须靠 alter system kill session 主动清理,且需配合查询 v$session 识别目标会话。
怎么查出真正空闲的会话?
不能只看 STATUS = 'INACTIVE' —— 很多后台作业、应用连接池里的连接长期处于 INACTIVE 状态但属于“合法空闲”,直接杀会引发应用报错。关键要看 LAST_CALL_ET(单位:秒)和 LOGON_TIME,再结合 PROGRAM、MACHINE 和业务上下文判断。
-
LAST_CALL_ET > 3600表示该会话最近一小时没执行任何 SQL 或 PL/SQL - 排除系统会话:
USERNAME IS NOT NULL AND USERNAME NOT IN ('SYS', 'SYSTEM') - 排除专用后台进程:
TYPE = 'USER'(过滤掉BG、SC类型) - 避免误杀长事务:
STATE = 'WAITING'且EVENT LIKE 'SQL*Net%'才更可能是空闲网络连接
用 PL/SQL 动态生成并执行 KILL SESSION 语句
不能硬编码 ALTER SYSTEM KILL SESSION 'sid,serial#',因为 sid/serial# 每次都变,必须从 V$SESSION 查后拼接。注意:KILL SESSION 是异步操作,执行后会话未必立刻消失,可能进入 KILLED 状态并等待下次轮询才释放资源。
- 用
DBMS_OUTPUT.PUT_LINE先打印将要执行的语句,调试阶段务必打开它 - 实际执行前加
EXECUTE IMMEDIATE,格式严格为'ALTER SYSTEM KILL SESSION ''' || sid || ',' || serial# || '''' - 捕获
ORA-00031(session 正在做清理)、ORA-00028(session 已断开)等常见错误,用EXCEPTION WHEN OTHERS THEN NULL;忽略而非中断整个循环 - 每次 KILL 后建议
DBMS_LOCK.SLEEP(0.1)避免瞬时高并发调用导致 latch 争用
如何安全地部署成定时任务?
直接用 DBMS_SCHEDULER 调用存储过程最稳妥,比 OS 层脚本更可靠——不用依赖外部连接、权限清晰、日志可查。但要注意调度器本身运行在数据库内,若会话被大量 KILL 可能影响调度器稳定性。
- 创建存储过程时,务必用
DEFINER'S RIGHTS(默认),且执行用户要有ALTER SYSTEM权限 - 调度任务的
repeat_interval建议设为每 5–10 分钟一次,太频繁易误伤;太长则空闲连接堆积 - 记录日志到自定义表(如
SESSION_KILL_LOG),字段至少含kill_time、sid、serial#、username、last_call_et - 禁止在 RAC 环境中跨实例调度——
V$SESSION是实例级视图,每个节点只能 kill 本节点的会话
真正难的不是写几行 KILL 语句,而是区分“该杀的空闲”和“不该动的保活连接”。很多故障源于把连接池维持的空闲连接当成了僵尸会话。上线前一定要在测试环境模拟真实应用连接行为,观察 LAST_CALL_ET 和 SQL_ID 的变化规律。











