之前项目在进行压测的过程中,客户的数据库发生宕机,但是当客户数据库恢复后,数据库(Oracle)出现了大量连接数,直接锁住数据库。将之前的总结文档记录到博客:
历史疑点
1.情景无法重现
现只能重现过以下情景,且只存在当连接池中连接数逼近数据库设置的并发数时才会出现数据库无法通过客户端或者sqlplus等登录失败的情况;
- 数据库服务正常运行场景下的1521端口连接统计,数据库连接池中连接统计;
- 数据库服务正常运行场景下的压力测试运行中的1521端口连接统计,数据库连接池中连接统计;
- 数据库服务正常运行场景下的压力测试执行完毕5分钟后的1521端口连接统计,数据库连接池中连接统计;
- 数据库服务在程序运行过程中修改用户密码场景下执行压力测试运行中1521端口连接统计,数据库连接池中连接统计;
- 数据库服务在程序运行过程中停止数据库服务场景下执行压力测试运行中1521端口连接统计,数据库连接池中连接统计;
- 在压力测试条件下用错误数据库密码启动程序场景下1521端口连接统计,数据库连接池中连接统计。
2.数据库连接过多具体有多少?
能否确定有效连接是否超过连接池上线300,数据库中的并发连接数设置的是多少?
3.连接使用结束后未能及时关闭是指多久未关闭?
配置为5分钟内未使用,则回收。
连接配置变更
2017年9月19日前的配置
参数名 |
参数值 |
解释 |
initialSize |
10 |
初始连接数 |
minIdle |
10 |
最小连接数 |
maxActive |
200 |
最大连接数 |
maxWait |
60000 |
超时等待时间 |
timeBetweenEvictionRunsMillis |
60000 |
关闭空闲连接的检测时间间隔 |
minEvictableIdleTimeMillis |
300000 |
连接的最小生存时间 |
validationQuery |
SELECT 1 from dual |
验证数据库服务可用性的sql |
testWhileIdle |
true |
申请连接时检测空闲时间,根据空闲时间再检测连接是否有效 |
testOnBorrow |
false |
申请连接时直接检测连接是否有效 |
testOnReturn |
false |
归还连接时检测连接是否有效 |
poolPreparedStatements |
true |
开启PSCache |
maxPoolPreparedStatementPerConnectionSize |
20 |
设置PSCache值 |
2017年9月19日后追加的配置
参数名 |
参数值 |
解释 |
connectionErrorRetryAttempts |
3 |
连接出错后再尝试连接三次 |
breakAfterAcquireFailure |
false |
数据库服务宕机自动重连机制 |
asyncInit |
true |
异步初始化策略 |
2018年7月27日后更改的配置
参数名 |
参数值 |
解释 |
breakAfterAcquireFailure |
true |
数据库服务宕机自动重连机制 |
当前问题分析
如关闭数据库服务器宕机自动重连机制后,如果出现数据库服务无法正常访问(后又能正常访问),则必须重启应用服务器。
拟解决方案
修改连接配置
参数名 |
参数值 |
解释 |
breakAfterAcquireFailure |
true |
数据库服务宕机自动重连机制 |
timeBetweenConnectErrorMillis |
300000 |
连接出错后重试时间间隔 |
- 启用数据库服务断线自动重连机制:保障数据库服务修复后系统自动重连,无需重新启动应用服务;
- 修改连接出错后重试的时间间隔:由30秒改为5分钟,降低数据库服务故障期间的申请链接的请求;
- 建议修改oracle数据库最大并发数:从数据库服务端提升并发能力。
- 建议启用oracle数据库连续登陆失败锁定用户机制:避免由于修改用户密码引起的其他问题。
- 建议提高数据库高可用性。
附数据库连接参数:
druid:
initial-size: 5
min-idle: 1
max-active: 20
query-timeout: 6000
#设置removeAbandoned="true"时,当连接池连接数到达(getNumIdle() < 2) and (getNumActive() > getMaxActive() - 3) [空闲的连接小于2并且活动的连接大于(最大连接-3)] 时便会启动连接回收,
#那种活动时间超过removeAbandonedTimeout="1800"的连接将会被回收,
#同时如果logAbandoned="true"设置为true,程序在回收连接的同时会打印日志。
#removeAbandoned是连接池的高级功能,理论上这中配置不应该出现在实际的生产环境,因为有时应用程序执行长事务,可能这种情况下,会被连接池误回收,该种配置一般在程序测试阶段,为了定位连接泄漏的具体代码位置,被开启。
#生产环境中连接的关闭应该靠程序自己保证。
#先关着
remove-abandoned: false
#必须要remove-abandoned为false才能生效,这样连接出问题的时候,每隔3000秒请求
async-init: true
time-between-connect-error-millis: 3000
#先关着
log-abandoned: false
transaction-query-timeout: 6000
remove-abandoned-timeout: 1800
filters: wall,stat
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=10000
stat-view-servlet:
login-username: bda
login-password: bda
url-pattern: /druid/*
reset-enable: false
web-stat-filter:
url-pattern: /*
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
#超时重试次数
connection-error-retry-attempts: 3
#必须为false(失败后会不断请求数据库,请求在TIME-WAIT,在数据库服务重启后,所有等待请求会访问数据库),
#为true上面参数才会生效(:true表示pool向数据库上面的重试请求连接此时失败后标记整个pool为block并close,
#就算后端数据库恢复正常也不进行重连,客户端对pool的请求都拒绝掉。false表示不会标记 pool为block,新的请求都会尝试去数据库请求connection。
#默认为false。因此,如果想让数据库和网络故障恢复之后,pool能继续请求正常资源必须把此项配置设为false)
break-after-acquire-failure: false
#检查连接正常的sql
validation-query: select 1 from dual
#配置多久检测一次空闲连接(可以选择是否尽早关闭连接,看压力在server还是数据库端)
time-between-eviction-runs-millis: 60000
#数据库连接最小生存时间
min-evictable-idle-time-millis: 300000