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)
以上。