记一次Oracle session数过多引起进程数超过processes限制最终导致客户端无法连接的问题
RAC突然告警,客户端尝试连接时会报:ORA-12520: TNS:listener could not find available handler for requested type of server 错误。
现整理解决过程,以便于后续遇到此问题时快速处理。
1. 登入服务器,使用sqlplus / as sysdba 发现可以进入数据库。
2. 查看alert日志,发现日志中有个错误信息:ORA-00020: maximum number of processes (2000) exceeded,由此判断是process数超了。
alert日志的路径可以通过如下sql找到:
select * from gv$diag_info where name like '%Alert%'
3. 确定出问题的Oracle用户,
select t1.inst_id,t1.username,count(*) from gv$session t1
join gv$process t2 on t2.addr=t1.paddr and t2.inst_id=t1.inst_id
group by t1.inst_id,t1.username order by 3 desc
可查到是xxxx用户连接数过多导致的。
4. 阻止用户再建立新的连接。
可以根据对客户端的控制程度来灵活选用处理方式。如果对客户端程序具有绝对的控制权,可将出问题的程序停掉,正常情况下问题就解决了。
我选择的方式是将用户lock,执行以下sql:
alter user xxxx account lock;
5. 此时我们查看gv$session,发现已经建立的连接还在。
select * from gv$session where username='XXXX'
6. 清理这些连接。
此时不要用alter system kill session,因为经过我后续测试发现,如果出问题的客户端程序未退出,该客户端与Oracle服务端建立的网络连接是一直保持的,此时即使我们kill session,process依然不会被释放,反而会导致gv$session视图和gv$process视图失去关系,进而导致我们查不到该用户的session对应那些进程。
在操作系统层面直接kill session对应的系统进程。
使用如下sql可查到XXXX账号的session对应的系统进程号:
select t1.inst_id,t1.sid,t1.serial#,t1.username,t2.spid 系统进程id from gv$session t1
join gv$process t2 on t2.addr=t1.paddr and t2.inst_id=t1.inst_id
where t1.username='XXXX'
7. 做完以上的操作进程的占用即可降下来。接下来就考虑与客户端沟通停掉出问题的程序然后对锁掉的账号进行恢复了。
顺便给出一个kill所有session对应的操作系统的命令,此法过于暴力,尽可能不要使用。
ps -ef | grep LOCAL=NO | grep -v grep | awk '{print $2}' |xargs kill -9
问题解决过后,我使用python在测试库中做了一下问题的复现,代码如下:
#!/bin/python # -*- coding: utf-8 -*- import cx_Oracle as co conn=co.SessionPool('XXXX','XXXXXXXXX','x.x.x.x',min=1,max=5000) a=[] for i in range(5000): print(i) try: ccc=conn.acquire() a.append(ccc) except Exception as e: print(e) time.sleep(20)
以上。