2021年12月21日 线上问题数据库连接池无法获取
一、背景
小编维护的一个网站,最近频繁发生数据库连接池问题,异常如下:
Exception occurred during processing request: Could not open JDBC Connection for transaction;
nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot get a connection, pool error Timeout waiting for idle object
org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction;
nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot get a connection, pool error Timeout waiting for idle object
二、排查问题
2.1 初步分析
小编遇到的这个问题,首先是先解决服务无法访问问题 ~ 先重启服务。
之后小编也相应排查了代码,因为最近没有代码提交,所以排除了代码没有关闭连接池的问题
2.2 nginx监控
接着,相应去排查nginx实时打印的日志,看是否有异常ip或者爬虫攻击。
监控发现,只有少量的异常ip请求,排查了nginx配置文件,也添加了相应的防爬虫机制。
排除!
2.3 数据库长连接
小编也问了几个小伙伴,基本都指向一个核心点 ~ 数据库连接池中存在长连接。
关键点找到了,由于数据库这方面也没有很多的经验,小编前几天的关注点也都在客户端,导致这个问题没有进展。
昨天的时候,小编突然想到可以通过 PL/SQL Developer 来查看相应的数据库连接数。
参考了以下的链接:
(PLSQL)查询数据库(连接池)连接情况
Oracle中V$session及session相关信息
三、验证问题
3.1 通过相关的SQL排查连接数
-- 查询数据库当前进程的连接数
select count(*) from v$process;
-- 查看数据库当前会话的连接数
select count(*) from v$session;
-- 查看数据库的并发连接数
select count(*) from v$session where status='ACTIVE';
-- 查看当前数据库建立的会话情况
select sid,serial#,username,program,machine,status from v$session;
-- 查询数据库允许的最大连接数
select value from v$parameter where name = 'processes';
连接数据不查不要紧,一查惊呆小编了,最大连接数为1000,当前进程连接数963,当前会话连接数960,并发的连接数是938。
这个数据也验证了小编及小伙伴的判断。
为了对比异常服务器与正常运行的服务器,小编也去相应数据库中查询了,发现进程连接数、会话连接数以及并发连接数都维持在100以内。
四、解决问题
问题得到了验证。
那么接下来就是解决问题。
此时,应用又开始报了个异常:
Caused by: java.sql.SQLException: Listener refused the connection with the following error:
ORA-12519, TNS:no appropriate service handler found
度娘之后,发现是:可能是数据库上当前的连接数目已经超过了它能够处理的最大值。
小编此时用plsql也无法查询了,直接崩溃了!
无奈之下,只能重启Oracle数据库,(慎用!!!)
4.1 重启数据库应用
登录数据库所在服务器
利用ssh登录数据库服务器
①、切换到Oracle用户
su - oracle
②、查看监听状态
lsnrctl status
③、关闭监听进程
lsnrctl stop
④、杀掉所有session
- 最重要一点,要关闭应用,不然会话连接一直存在
# 网上搜索杀掉session的命令如下:
ps -ef|grep $ORACLE_SID|grep -v ora_|grep LOCAL=NO|awk '{print $2}'|xargs kill
# 实际上,我使用的时候并没有用,索性就用比较粗暴的方式,查询所有的进程id
ps -ef|grep $ORACLE_SID|grep -v ora_|grep LOCAL=NO|awk '{print $2}'
# 会列出所有连接的进程id,如果没有则表示没有会话连接,
# 用以下命令杀死进程
kill -9 {进程id}
⑤、关闭数据库
使用命令登录数据库
sqlplus /nolog
进入sqlplus界面,然后连接dba
conn /as sysdba
关闭数据库
shutdown immediate
成功关闭显示:
Database closed.
Database dismounted.
然后退出sqlplus:
exit
⑥、启动数据库监听器,并查看监听器状态
lsnrctl start
lsnrctl status
⑦、启动数据库
进入sqlplus,并连接dba;
sqlplus /nolog
conn /as sysdba
启动数据库
startup
五、SQL查询验证是否解决
-- 查询数据库当前进程的连接数
select count(*) from v$process;
-- 查看数据库当前会话的连接数
select count(*) from v$session;
-- 查看数据库的并发连接数
select count(*) from v$session where status='ACTIVE';
-- 查看当前数据库建立的会话情况
select sid,serial#,username,program,machine,status from v$session;
-- 查询数据库允许的最大连接数
select value from v$parameter where name = 'processes';
此时再次查询时,当前进程连接数、当前会话连接数以及并发的连接数都降到100以内。
完美解决!