前言
本篇对java的数据库的连接池进行讲解。
主要站在运维角度讲解主流数据库连接池的配置参数,以及常见报错场景
1、为什么使用数据库连接池?
如果不使用数据库连接池,那么应用每次访问数据库都需要进行连接的创建,频繁的进行TCP握手、挥手。造成大量消耗,降低系统性能和并发能力。
数据库连接池管理维护一定数量的数据库连接,在应用需要连接数据库的时候进行分配,极大的降低了消耗,并能更好的管理对数据库的连接。
2、什么是数据库连接池?
下图简单介绍了数据库连接池在应用中起到的作用,以及一些常见的配置指标
3、数据库连接池有哪些?
目前的数据库连接池有Hikari,druid,dbcp,tomcat-jdbc,c3p0,BoneCP,Proxool,下面分别简单介绍下
整体来说,更为推荐Hikari 和 druid
Hikari性能最强
druid扩展性、监控性最强
3.1、c3p0
历史悠久,代码及其复杂,不利于维护。并且存在deadlock的潜在风险,不建议使用
3.2、BoneCP
现在已经不更新了,转到了HiKariCP上。不建议使用。
3.3、Proxool
评测说在并发较高的情况下会出错,且现在维护力度已经很小,不建议使用。
3.4、dbcp
tomcat内置的连接池,单独使用dbcp需要3个包:common-dbcp.jar,common-pool.jar,common-collections.jar
单线程,并发量低,性能不好,适用于小型系统
3.5、tomcat-jdbc
由于dbcp的性能不太好,apache又新开发了一款数据库连接池-tomcat jdbc pool,有的地方也称之为JDBC Connection Pool。
3.6、Hikari
SpringBoot默认使用Hikari(springboot2.0之后)
SpringBoot项目如果我们通过启动器starter使用JPA或者Mybatis,是默认会使用Hikari数据库连接池的,不需要引入依赖,已经在SpringBoot中包含了。
号称最快的数据库连接池,强于性能。
3.7、druid
Druid能够提供强大的监控和扩展功能,强项在于监控
4、数据库连接池配置
关于各个数据库连接池的核心配置项,我这里对目前最为常用的三个数据库连接池(Druid,Hikari,DBCP)
配置分类 | 参数 | 参数描述 | Druid | Hikari | DBCP | ||||||
Druid能够提供强大的监控和扩展功能,强项在于监控 | SpringBoot默认使用Hikari(springboot2.0之后) SpringBoot项目如果我们通过启动器starter使用JPA或者Mybatis,是默认会使用Hikari数据库连接池的,不需要引入依赖,已经在SpringBoot中包含了。 号称最快的数据库连接池,强于性能。 | tomcat内置的连接池,单独使用dbcp需要3个包:common-dbcp.jar,common-pool.jar,common-collections.jar 单线程,并发量低,性能不好,适用于小型系统 | |||||||||
参数 | 描述 | 默认值 | 参数 | 描述 | 默认值 | 参数 | 描述 | 默认值 | |||
基础重要 | 获取连接超时时间 | maxWait | 无限 | connectionTimeout | 30000ms | maxWait | 无限 | ||||
最小连接数 | minIdle | 8 | minimumIdle | 官方推荐不设置此值,默认同最大连接数相同 | 10 | minIdle | 0 | ||||
最大连接数 | maxActive | 8 | maximumPoolSize | 池中最大连接数,包括闲置和使用中的连接 | 10 | maxActive | 8 | ||||
初始化连接数 | initialSize | 0 | initialSize | 0 | |||||||
连接最长生命周期 | maxLifetime | 一个连接的生命时长(毫秒),超时而且没被使用则被释放(retired) 如果不等于0且小于30秒则会被重置回30分钟 | 1800000ms | ||||||||
连接最大空闲时间 | idleTimeout | 连接允许在池中闲置的最长时间 如果idleTimeout+1秒>maxLifetime 且 maxLifetime>0,则会被重置为0(代表永远不会退出);如果idleTimeout!=0且小于10秒,则会被重置为10秒 | 600000ms | ||||||||
最大空闲连接数 | maxIdle | 最大空闲连接:连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制 | 8 | ||||||||
最小空闲连接数 | minIdle | 最小空闲连接:连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接,如果设置为0则不创建 | 0 | ||||||||
其他 | readOnly | 从池中获取的连接是否默认处于只读模式 | false | defaultReadOnly | 从池中获取的连接是否默认处于只读模式 | ||||||
autoCommit | 此属性控制从池返回的连接的默认自动提交行为 设置为false后,事务不会自动提交,导致对数据库的插入修改操作无效,但是查询和删除可以。 | true | defaultAutoCommit | 此属性控制从池返回的连接的默认自动提交行为 | |||||||
poolName | 连接池的用户定义名称,主要出现在日志记录和JMX管理控制台中以识别池和池配置 | HikariPool-1 | |||||||||
连接有效性检查 | 有效性检查sql | validationQuery | 连接有效性检查执行sql 如果为null,则下面参数不生效 testOnBorrow testOnReturn testWhileIdle | null | connection-test-query | 数据库连接测试语句 | null | validationQuery | 连接有效性检查执行sql 如果为null,则下面参数不生效 testOnBorrow testOnReturn testWhileIdle | null | |
testOnBorrow | 申请连接时执行validationQuery检测连接是否有效 | TRUE | keepaliveTime | 每隔 keepaliveTime 检查连接的有效性,如果db连接不可用,则直接关闭 | 0(关闭) | testOnBorrow | 申请连接时执行validationQuery检测连接是否有效 | true | |||
testOnReturn | 归还连接时执行validationQuery检测连接是否有效 | FALSE | testOnReturn | 归还连接时执行validationQuery检测连接是否有效 | false | ||||||
testWhileIdle | 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 | FALSE | testWhileIdle | 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 | false | ||||||
timeBetweenEvictionRunsMillis | 1) Destroy线程会检测连接的间removeAbandoned隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。2) testWhileIdle的判断依据 | 60000 | timeBetweenEvictionRunsMillis | 在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位. 如果设置为非正数,则不运行空闲连接回收器线程 | -1 | ||||||
minEvictableIdleTimeMillis | 连接保持空闲而不被驱逐的最小时间 | 300000 | minEvictableIdleTimeMillis | 连接保持空闲而不被驱逐的最小时间 | |||||||
异常调试 | removeAbandoned | 用来关闭长时间不使用的连接。RemoveAbandanded功能不建议在生产环境中使用,仅用于连接泄露检测诊断 | FALSE | removeAbandoned | 用来关闭长时间不使用的连接。RemoveAbandanded功能不建议在生产环境中使用,仅用于连接泄露检测诊断 | FALSE | |||||
removeAbandonedTimeout | 打开RemoveAbandanded功能,超过这个时间则会关闭连接,单位秒 | removeAbandonedTimeout | 打开RemoveAbandanded功能,超过这个时间则会关闭连接,单位秒 | ||||||||
logAbandoned | 关闭abanded连接时输出错误日志 | logAbandoned | 关闭abanded连接时输出错误日志 | ||||||||
5、常见数据库连接池报错
5.1、获取数据库连接超时
原因分析:
1、瞬间并发突增导致连接被占满
2、出现了慢SQL。或者连接泄漏
5.1.1、Druid
com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 20, maxActive 20, creating 0
报错解读分析:
当前连接池活动连接数为20,最大连接数限制为20,所以没有空闲连接能进行分配,新的请求进来,等了60秒(最大等待时间)之后,抛出了异常
异常处理解决:
1、调大最大连接数:
maxActive = 30
2、打开连接回收机制:
removeAbandoned = true 【开启超时回收】
removeAbandonedTimeout = 180 【超过180秒则进行回收】
logAbandoned = true 【回收的时候打印相关日志】
如下就是连接超过removeAbandonedTimeout设置的时间,断开连接之后打印的日志,会打印出对应线程的堆栈先,就可以看出是哪个地方有长时间不释放的数据库连接。
5.1.2、Hikari
Unable to acquire JDBC Connection Connection is not available, request timed out after 30000ms.
报错解读分析:
等待分配数据库连接30秒之后超时报错
异常处理解决:
1、调大最大连接数:
maximum-pool-size: 20
当然 最重要的还是优化代码
5.2、获取到无效连接
5.2.1、Druid
The last packet successfully received from the server was 1,855,774 milliseconds ago. The last packet sent successfully to the server was 1,855,775 milliseconds ago.
报错解读分析:
获取到的数据库连接已经无效,并列出了最后一次成功接收和发送数据的时间间隔
异常处理解决:
1、设置连接有效性检查,但会对性能造成影响
validationQuery=select 1
testWhileIdle=true
testOnBorrow=true
testOnReturn=true
2、调整数据库超时配置
核查应用到数据库整条链路上的超时时间(中间可能经过负载均衡以及数据库中间件mycat等),整个数据库调用链路超时设置,应该从前到后,超时时间越来越大
如MYSQL端的wait_timeout
5.2.2、Hikari
The last packet successfully received from the server was 212,079 milliseconds ago. The last packet sent successfully to the server was 3,004 milliseconds ago.). Possibly consider using a shorter maxLifetime value.|
Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@72b70c61 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value
报错解读分析:
这两条报错都可以看出是当前获取的连接已经不再有效
异常处理解决:
1、数据库连接池设置的连接存活时间超过了数据库端的超时时间,那么调小maxLifetime,但是这种情况较为少见,因为一般数据库端都会是8小时,而Hikari默认是30分钟
2、数据库端有定时任务kill掉一定时间不活动的数据库连接,或者别的配置(网络,负载均衡,服务器tcp_keepalive_time配置等等原因)导致数据库连接被断掉,也是调小maxLifetime
建议调整成30秒比较合适