许多DBA在排查Oracle数据库问题时,听到“僵尸用户”或“僵尸账号”这类说法,心里难免会有些顾虑。实际上,Oracle官方并没有一个名为“僵尸用户”的标准定义。真正值得关注的是两类截然不同的数据库对象:一类是被锁定或密码已过期、完全无法登录的账号;另一类则是长期处于空闲状态、却依然占用连接资源的会话进程。不必被“僵尸”这个称谓所迷惑——核心在于它们背后暴露的真实状态与异常行为。

如何查找被锁定或密码过期的Oracle用户:重点关注DBA_USERS.ACCOUNT_STATUS
一个数据库账号能否正常登录,最直接的判断依据就是DBA_USERS视图中的ACCOUNT_STATUS字段。该字段不依赖于登录失败日志,也无需查阅会话列表,信息直接存储在系统字典表中。只要状态值显示为'LOCKED'、'LOCKED(TIMED)'、'EXPIRED'或'EXPIRED(GRACE)',就表明该用户当前处于“不可用”状态。这些状态的具体含义略有不同:
LOCKED状态:由管理员手动执行ALTER USER ... ACCOUNT LOCK命令导致,除非主动解锁,否则该账号将被永久锁定。LOCKED(TIMED)状态:因密码连续输入错误次数超出限制(由参数FAILED_LOGIN_ATTEMPTS控制)而触发,若PASSWORD_LOCK_TIME参数设置为0,则账号会被立即锁定。EXPIRED状态:密码策略已到期,但用户未及时修改密码。在这种情况下,即使输入正确的密码也无法登录。更关键的是,如果再次输错密码,该状态可能演变为EXPIRED & LOCKED的复合锁定状态。
一条SQL语句即可快速定位所有存在风险的数据库账户,核心查询逻辑非常简洁:
SELECT username, account_status, lock_date, expiry_date FROM dba_users WHERE account_status IN ('LOCKED','LOCKED(TIMED)','EXPIRED','EXPIRED(GRACE)')ORDER BY lock_date DESC, expiry_date DESC;这里有一个容易被忽略的细节:如果发现lock_date字段显示为NULL,千万不要误以为账号未被锁定。对于LOCKED(TIMED)状态以及EXPIRED & LOCKED复合状态,系统并不会记录具体的锁定时间点。
查找长期不活跃但仍占用连接的账号:结合v$session和last_call_et
另一类“僵尸会话”属于数据库会话层面的问题。当一个数据库会话的STATUS显示为INACTIVE,却长时间保持空闲状态时,它既不执行SQL语句,也不主动断开连接,导致数据库连接资源被无谓占用。这种情况通常源于应用连接池泄漏或程序异常退出后残留的“幽灵”连接。
要准确识别这些不活跃会话,需要从多个维度进行综合判断:
- 筛选条件中必须包含
last_call_et > 3600(即空闲时间超过1小时),否则会混入大量正常的短时等待会话,导致无法聚焦目标。 - 务必要排除后台进程,即添加
type != 'BACKGROUND'条件。否则PMON、DBWR等后台守护进程也会被纳入统计。 - 同时需要添加
username IS NOT NULL条件,确保不会将SYS、SYSTEM等系统内置用户的会话纳入统计。 - 此外,仅依赖
sql_id IS NULL条件是不够的。有些会话刚刚建立连接,尚未执行任何SQL语句,此时sql_id同样为NULL,但这与真正的“僵尸会话”是两回事。需要结合logon_time登录时间和实际业务规律进行综合判断。
示例查询语句如下:
SELECT sid, serial#, username, machine, program, last_call_et, sql_idFROM v$session WHERE status = 'INACTIVE' AND type != 'BACKGROUND' AND username IS NOT NULL AND last_call_et > 3600;
如果同一username+machine+program组合下返回了大量此类会话记录,那么基本可以判定是应用端存在问题——很可能是因为没有正确调用close()方法来释放数据库连接。
临时段占用型“假活跃”:必须关联v$sort_usage验证
这里有一个非常常见的陷阱。某些数据库会话的STATUS虽然显示为INACTIVE,但实际上后台正在忙于清理大型排序操作所产生的临时段。v$sort_usage视图中仍然保留着它的相关记录,此时如果贸然执行ALTER SYSTEM KILL SESSION强制终止,很可能会遇到ORA-00031: session marked for kill错误。同时blocks值也会持续居高不下。
因此不能仅凭v$session视图就贸然采取行动。必须通过v$sort_usage进行交叉验证。重点关注u.tablespace = 'TEMP'且u.blocks > 0的记录。如果对应的sid恰好也出现在“疑似不活跃”的会话列表中,那么结论就很明确了:该会话并非真正闲置,而是临时段清理工作尚未完成。
验证SQL语句如下:
SELECT s.sid, s.serial#, s.username, u.blocks * 8 / 1024 AS mb_usedFROM v$sort_usage uJOIN v$session s ON u.session_addr = s.saddrWHERE u.tablespace = 'TEMP' AND u.blocks > 0;
真正让DBA感到棘手的,往往不是查不到问题,而是查到了却不敢轻易处理。例如,一个username = 'DBSNMP'且长期处于INACTIVE状态的会话,很可能是监控轮询任务残留的连接;而一个ACCOUNT_STATUS = 'EXPIRED(GRACE)'的用户,当前看似能够登录,但宽限期一旦结束就会立即被锁定。每个状态字段背后都隐藏着配置参数、权限设置和Profile策略的三层逻辑,只要忽略其中一层,操作就可能导致意外后果。这才是排查Oracle僵尸用户和不活跃账号时需要把握的关键所在。
