记一次数据池连接失败异常, java.sql.SQLTransientConnectionException: Connection is not available
背景
系统上线完成后,一开始运行正常,过了一会儿开始出现:系统所有接口陆续出现长时间无响应或直接响应500。
排查过程
check日志发现数据库连接池连接超时,初步怀疑有连接一直占用连接未释放,通过日志排查了一会,无果。
暂时先重启服务器
系统恢复了,但是过了一会又开始出现系统无响应的情况,不过发现某些接口可以正常访问,最后排查到在某一段时间除了一个接口无响应,其他接口都可以正常响应。
定位错误接口
排查日志,发现这个接口在执行一个sql查询后,之后的日志一直没有打出来,怀疑是这个sql的问题,确实,这是一个包含了很多张表join的大sql,在线上试了下,一直跑不出来。
解决:先将这句sql回滚,保证不拖垮整个系统,再进行后期优化。
具体的sql优化我没跟,但是注意到另一个问题
事故总结
某大sql一直跑不出来,导致数据库连接池连接一直每占用,得不到释放,在频繁访问接口以致频繁触发大sql的情况下,很快将连接池占满,导致后续其他接口无法获取数据库连接,无法响应。这也解释了为什么一开始某些接口还能响应,到最后都不能响应了。
优化
除了sql优化这块,还注意到另一个问题,sql执行超时时间。
sql执行超时时间
通常,系统会设置一个sql执行超时时间,当在超时时间内sql还未执行完成就会中断执行,释放连接,避免因为数据库连接瓶颈拖垮整个应用。拿我们的场景来讲,一段时间内数据库连接池被占满,暂时不可用,但过一段时间后,大sql会执行超时,会自动释放连接,连接池又有可用的连接了,所以一段时间后,系统应该恢复才对,但是我们的情况是系统一直没有恢复。
b. check系统的配置
db server端(mysql):max_execution_time = 0 ==> sql执行时间无限长
应用端sql执行超时配置:无
c. 优化(以下三种亲测有效,根据实际情况选用)
DB server端(mysql):设置max_execution_time来控制sql执行的超时时间,但会影响所有连接到该db的执行时长,影响太大,不建议。
应用端:设置单条sql执行的超时时长,以mybatis举例(单位为秒):
xml:<select id="test" timeout="10">
注解:@Options(timeout = 10)
应用端:设置整个应用所有sql执行的超时时长(单位s)
mybatis-plus:
configuration:
default-statement-timeout: 60
尝试去找到sql执行超时也踩了一些坑(找错参数)
mysql: connect_timeout, wait_timeout, interactive_timeout
hikari: connection-timeout, max-lifetime(之前一直在往连接池的超时配置上找参数)
不一一总结了。
附:
数据库连接池:Hikari
DB:Mysql
ORM框架:mybatis
参考:
如何配置MySQL数据库超时设置 - 程序员大本营 (pianshen.com)