❝Hikari, 日语中"光"的发音。
HikariCP, 如其名,性能像光一样快,springboot官方在2.0后也肯定了一波,选择了HikariCP作为默认的数据源连接池。
除此之外,安全可靠方面也是得到充分验证的,包也仅有150k左右。
一个词,短小精悍!
我刚毕业那会,还是德鲁伊-durid的天下,正式入职后,第一个单独上手的项目就毫不犹豫的选择了durid开发,临上线前,恰好springboot 2.0也刚发布不久,对技术嗅觉灵敏的领导,给我抛了hikaricp的wiki,当时也懵懂,没有仔细研究,只是按要求更换了。
随着越来越多的了解和使用,慢慢从设计层面,应用场景方面也体会到了两者的差异,也掌握的hikaricp的一些正确配置姿势。
hikaricp 配置
除过基本基本配置外,搬运一下比较关键常用的配置,也方便我日后查找。
https://github.com/brettwooldridge/HikariCP#configuration-knobs-baby
autoCommit
此属性控制从池返回的连接的默认自动提交行为。默认值:真
connectionTimeout
此属性控制客户端等待来自池的连接的最大毫秒数。如果在没有连接可用的情况下超过此时间,则将抛出 SQLException。可接受的最低连接超时时间为 250 毫秒。默认值:30000(30 秒)
idleTimeout
此属性控制允许连接在池中闲置的最长时间。**此设置仅在
minimumIdle
定义为小于时适用maximumPoolSize
。**一旦池达到连接, 空闲连接将不会被取消minimumIdle
。连接是否以空闲状态退役,最大变化为 +30 秒,平均变化为 +15 秒。在此超时*之前,*连接永远不会因空闲而退出。值 0 表示永远不会从池中删除空闲连接。允许的最小值为 10000 毫秒(10 秒)。默认值:600000(10 分钟)keepaliveTime
此属性控制 HikariCP 尝试保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于该
maxLifetime
值。“keepalive”只会发生在空闲连接上。当针对给定连接进行“保持连接”的时间到了时,该连接将从池中删除、“ping”,然后返回到池中。'ping' 是其中之一:调用 JDBC4isValid()
方法,或执行connectionTestQuery
. 通常,池外的持续时间应该以个位数毫秒甚至亚毫秒为单位进行测量,因此应该几乎没有或没有明显的性能影响。最小允许值为 30000 毫秒(30 秒), 默认值:0(禁用)maxLifetime
此属性控制池中连接的最长生命周期。使用中的连接永远不会退役,只有当它关闭时才会被移除。在逐个连接的基础上,应用较小的负衰减以避免池中的大规模灭绝。我们强烈建议设置此值,它应该比任何数据库或基础设施强加的连接时间限制短几秒。 值 0 表示没有最大生命周期(无限生命周期),当然取决于
idleTimeout
设置。允许的最小值为 30000 毫秒(30 秒)。默认值:1800000(30 分钟)connectionTestQuery
**如果您的驱动程序支持 JDBC4,我们强烈建议不要设置此属性。**这适用于不支持 JDBC4 的“传统”驱动程序
Connection.isValid() API
。这是将在从池中向您提供连接之前执行的查询,以验证与数据库的连接是否仍然有效。同样,尝试在没有此属性的情况下运行池,如果您的驱动程序不符合 JDBC4,HikariCP 将记录错误以通知您。 默认值:无minimumIdle
此属性控制HikariCP 尝试在池中维护的最小空闲连接数。如果空闲连接数低于此值并且池中的总连接数小于
maximumPoolSize
,HikariCP 将尽最大努力快速有效地添加额外的连接。但是,为了获得最高性能和对峰值需求的响应,我们建议不要设置此值,而是允许 HikariCP 充当固定大小的连接池。默认值:与maximumPoolSize 相同maximumPoolSize
此属性控制允许池达到的最大大小,包括空闲和使用中的连接。基本上这个值将决定到数据库后端的最大实际连接数。一个合理的值最好由您的执行环境决定。当池达到此大小且没有空闲连接可用时,对 getConnection() 的调用将
connectionTimeout
在超时前阻塞长达几毫秒。默认值:10
重点解释一下这几个配置项:
maxLifetime
了解这个值的作用前,先了解一下mysqlwait_timeout
的作用:mysql 为了防止空闲连接浪费,占用资源,在超过wait_timeout 时间后,会主动关闭该连接,清理资源。
通过show variables like 'wait_timeout%'
, 可以查看mysql 具体配置的时间
默认是28800s,也就是8小时。
也就是说,mysql发现某个连接超过8小时还有没有任何请求,就会自动断开,但是hikaricp如何知道我池子里维护的一把连接,有没有被mysql回收呢?
所以就有了maxLifetime
这个配置,官方也强烈建议必须按需设置此值!自然这个值也应该小于mysql的wait_timeout
。
如果先不考虑idleTimeout
配置,那hikaricp在空闲连接超过maxLifetime,就会从连接池中剔除,防止业务进程取到了已关闭的连接,导致业务受损。
keepaliveTime & connectionTestQuery
最开始没太理解,后来类比tcp的keepAlive机制,就清楚多了。
他的作用还是如上所说,为了防止获取到被mysql关闭的无效连接,导致业务出错的一种兜底扫描方案。
具体过程呢,就是每隔keepaliveTime时间间隔,去和数据库发送心跳,来探测连接是否有效。如果发现是无效的,就会及时从连接池中剔除,来保证业务进程获取到的都是有效连接。
如果你配置了connectionTestQuery
,如"select 1", 心跳检查过程就会调用connectionTestQuery。
所以如果你配置了connectionTestQuery
(exsample: select 1),但是没有配置keepaliveTime
,是没有用的,因为默认是关闭的。
而connectionTestQuery
配置项,官方建议如果驱动支持JDBC4,不要设置此属性!
因为相比于通过select查询方式探活,mysql 自带的ping命令(目测应该就是TCP的ping),性能更高(直接在sql server返回结果,就不会做语法解析,执行优化,再通过存储引擎操作)
而基本上java mysql驱动包5以上的版本都支持JDBC4。
maximumPoolSize
数据源连接池最大连接数,其实就是线程池中队列的大小。默认大小为10
minimumIdle
空闲连接数最大值,默认大小和maximumPoolSize
的默认值一样,也是10。
在hikaricp pool创建时,会启动一个HouseKeeper定时任务,每隔30s,判断空闲线程数低于minimumIdle,并且当前线程池总连接数小于maximumPoolSize,就建立和mysql的一个长连接,然后加入到连接池中。
官方建议minimumIdle
和maximumPoolSize
保持一致,这和jvm的xms,xmx参数也建议保持一致有类似的考量。
因为hikaricp的HouseKeeper在发现idleTimeout>0 并且 minimumIdle < maximumPoolSize时,先会去扫描一遍需要移除空闲连接,和mysql断开连接。然后再一次性补满空闲连接数至到minimumIdle。
mysql的一次连接就是3次握手,断开就是4次挥手,所以这个过程在实际生产中可能会相当耗时,自然会影响业务请求的处理响应。
idleTimeout
这是hikaricp用来判断是否应该从连接池移除空闲连接的一个重要的配置。负责剔除的也还是HouseKeeper这个定时任务,值为0时,HouseKeeper不会移除空闲连接,直到到达maxLifetime后,才会移除,默认值也就是0。
正常情况下,HouseKeeper会找到所有状态为空闲的连接队列,遍历一遍,将空闲超时到达idleTimeout且未超过minimumIdle数量的连接的批量移除。
只有掌握了这些配置的真正含义后,才能减少埋坑,不过坑防不胜防,还有以下两点需要注意:
一:检查相关配置,确保不要业务进程获取到已经关闭连接
hikaricp为了防止业务进程获取到已经关闭失效的连接,煞费苦心。
除了HouseKeeper定时任务30s一扫,来移除失效连接外。
还有maxLifetime的兜底移除失效连接。
但,坑也就在这,
HouseKeeper的扫描移除条件是:
idleTimeout > && minimumIdle < maximumPoolSize
所以,如果你minimumIdle = maximumPoolSize,那就意味着,一定要检查确保maxLifetime 一定要小于实际mysql的wait_timeout值。
二:连接池的大小越大越好吗?
关于这个问题,hikaricp的作者也是很头疼,
这老哥真的无语,我都默认值是10了,你们哪怕可以别设置这个值,非得几十个人用的系统,配成100的大小。
另外,他甚至专门开了个页面来讲这个问题:
https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing
里面有 Oracle 关于连接池大小的性能测试视频,感兴趣的可以看看。
总结来说,影响数据库瓶颈主要是: CPU, 磁盘 和网络。
根据这些因素,作者给出了一个计算公式
连接数 = ((cpu核数 * 2) + 磁盘有效主轴数)
此外,作者还解释了,默认值为啥是10:
正常情况下,10个连接,就能够撑起6000TPS的简单查询 !!!
极其严谨,肃然起敬!这也让我产生了对源码拜读的浓厚兴趣。
巧的是,在我刚整理完这篇文档后,同事上线的新系统 线上出了个bug。
线程在超时30s后仍获取不到数据库连接,同事第一时间提出尝试调大连接数的方案。
leader的回复是:
当真正了解作者默认值给10的良苦用心后,点赞