HikariCP连接池初识
HikariCP连接池初识
HikariCP是一个高性能的数据库连接池,以其轻量级和快速响应而闻名。作为Spring Boot的默认连接池,HikariCP在微服务架构中得到了广泛应用。本文将详细介绍HikariCP的核心参数配置、连接池大小的优化策略以及如何避免连接泄露问题。
HikariCP的简单介绍
HikariCP(Hikari Connection Pool)取义“像光一样轻和快的连接池”,它几乎完全用Java编写,以轻量级和高性能著称。目前,HikariCP已成为Spring Boot的默认连接池,随着Spring Boot和微服务的普及,其使用率也越来越高。
在HikariCP的GitHub首页,有一篇性能对比报告(性能对比),显示了其在性能上的优势。虽然这份对比数据有些过时,且没有包含阿里巴巴自研的Druid连接池的对比,但HikariCP的性能表现仍然令人印象深刻。Druid在功能性和社区活跃度上可能更胜一筹,但在性能方面,两者各有优劣,目前还没有权威的对比报告。
几个连接池参数
HikariCP的参数设置相对精简,但每个参数都至关重要。以下是几个核心参数的详细解释:
参数 | 含义 |
---|---|
minimumIdle | 控制连接池中维持的最小空闲连接数。如果空闲连接数低于此值且总连接数少于maximumPoolSize,HikariCP会尝试快速添加额外连接。推荐不设置此值,让连接池保持固定大小。默认值:与maximumPoolSize相同。 |
maximumPoolSize | 连接池的最大大小,包括空闲和正在使用的连接。此值决定了到数据库后端的实际连接数上限。默认值:10。 |
maxLifetime | 连接的最大生命周期。正在使用的连接不会被移除,只有在关闭时才会被移除。建议设置此值,并确保比数据库或基础设施的连接时间限制短几秒。默认值:1800000毫秒(30分钟)。 |
idleTimeout | 连接在池中允许空闲的最大时间。当池达到minimumIdle连接数时,空闲连接不会被回收。默认值:600000毫秒(10分钟)。 |
keepaliveTime | 控制HikariCP尝试保持连接活跃的频率,以防止连接因数据库或网络基础设施而超时。此值必须小于maxLifetime。默认值:0(禁用)。 |
keepaliveTime参数的设置应低于数据库空闲连接超时时间、TCP空闲连接超时时间以及一切其他设施的空闲超时时间。对于PostgreSQL来说,hikariCP的keepaliveTime参数应设置为小于PG库的idle_in_transaction_session_timeout
。
很明显,maximumPoolSize代表连接到数据库中的最大连接数。当然一般来说,真实场景中数据库中的连接数不会一直保持maximumPoolSize,因为应用不可能从始至终都是最高负荷运行。即使经过一个请求高峰期,根据idleTimeout或者maxLifetime的设置,那些空闲的连接经过一段时间后应该被释放。为了保证数据库的可用性,这个值应该设置为比数据库最大连接数小一些。比如PostgreSQL数据库,maximumPoolSize参应设置为小于PG库的max_connections
。这个参数还有调优空间,我们下面会提及。
minimumIdle是最小空闲连接数。例如,如果minimumIdle=100,数据库的active会话有10个,那么理论上数据库中的总连接数应该是100+10个。因为有可能有连接风暴的情况,真实的数据库连接应该比active+minimumIdle略多一点,但肯定小于maximumPoolSize。
为什么数据库连接数远大于minimumIdle?理论上数据库总连接数只应该略大于minimumIdle,但是经过我实际观察连接池多节点的情况,哪怕数据库活跃连接只有10几个,数据库总连接数却远大于minimumIdle。观察pg_stat_activity的min(backend_start)、min(state_change),基本保持在maxLifetime左右,说明连接回收是有作用的。看上去新请求总喜欢启用新连接,而不是直接拿已有的idle连接用。个人猜多节点部署是原因之一,每个节点minimumIdle很低,也可能存在某些组件上的节点请求要多一点,瞬时请求数超过了minimumIdle从而创建了新的连接。第二,这跟maxLifetime参数也有关系,maxLifetime的目的是为了旋转连接,释放那些一直在用的连接,这样就存在那些使用过的链接需要一段时间来释放,并且最好不要再使用了,以免延长释放周期。
连接池大小设置
连接数过多的影响
在数据库的世界中,“数据库连接数的增多,数据库性能都会一定的下降”。例如oracle的连接数对性能的影响,参考这个视频。当资源配置、jdbc并发都不变的情况下,连接数从2048下降到1024个,请求响应时间下降一半;如果连接数调整到96个,响应时间下降几十倍!!
连接数设置为多少才合适?
Unless you have a database server that has 1000 cores, it is very unlikely that you really want a maximumPoolSize of 2000.
除非你的数据库有1000C,不然你不应该有2000个连接。
最初始的情况下,数据库连接数应设置为cpu数,这样就能达到cpu的最大性能模式。但是这不是真实的。因为数据库的消耗不仅在cpu,也在磁盘和网络(也有内存、但相对影响不大)。例如,磁盘的读写也需要时间,cpu需要等待磁盘返回数据才可以进行下一步动作。在IO等待的这段时间(有可能时间很长),cpu最好是不要闲着,而是给其他进程使用。所以,基于磁盘等设备的等待时间,数据库连接数最好是高于cpu个数。
由于SSD等磁盘性能的提升,磁盘访问的速度是非常快的,也就是说IO等待时间下降,意味着连接数应该调整得更低。
调低了不能压榨CPU,调太高数据库性能损耗,那么到底调整到多少合适呢?hikariCP给出了这么一个公式
connections = ((core_count * 2) + effective_spindle_count)
其中core_count不应该计算超线程数;effective_spindle_count为主轴数,如果活动数据集完全被缓存,那么effective_spindle_count为零,随着缓存命中率的下降,它应该接近于实际的主轴数量。对应SSD还没有想过公式,不过可以肯定小于以上的最大值。当然这些都是理论值,实际情况要比这个更复杂,比如长连接问题,具体可参考连接池大小相关知识。
即使前端有10000个用户,连接池也不可能是10000个,即使是1000也太多了,需要一个更小的连接数,让其余的请求在连接池中等待,发挥数据库及其CPU的最佳性能才是最好的方式,参考连接数设置如上公式所示。
fixed pool
fixed pool是HikariCP的作者Brett Wooldridge的一个理念,是为了解决连接风暴问题。在minimumIdle参数解释中已经提及fixed pool:
为了达到最大性能和对高峰需求的响应性,我们推荐不设置minimumIdle,而是让HikariCP充当一个固定大小连接池(fixed sizeconnection pool)。默认值:与maximumPoolSize相同。
把minimumIdle=maximumPoolSize就是fixed size connection pool。minimumIdle的默认值就等于maximumPoolSize。
其实早在2014年,Brett Wooldridge就提到了这个概念,参考PG社区邮件。这段话很重要,我将逐字翻译:
根据我的经验,即使是维护最小空闲连接数的池,在响应突发需求时也是有问题的。如果你有一个最大30个连接的池,并且有一个最小10个空闲连接的目标,突发的需求需要20个连接意味着连接池可以立即满足10个,但随后要尝试在应用程序申请连接时间到达connectionTimeout之前建立另外10个连接。这反过来在数据库上产生了突发需求,不仅减慢了建立连接本身,也减慢了实际上可能会将连接返回给连接池的事务。
现在,如果你的峰值是100个连接,你的中位数是50个,这并不重要。但我知道不少工作负载的峰值是1000,中位数是25,在这种情况下你会想要逐渐减少空闲连接。
最终我们采用了一个maxPoolSize + minIdle模型,默认情况它俩相等(fixed pool)。
虽然我不怀疑存在这样的工作负载(1000个活动连接),如果有人真的这么做了,我很想听听他们的理由。除非他们有超过128个CPU核和固态存储,否则基本上就是在白费功夫。
这也意味着,即使连接池的大小是固定的,你也想要旋转(rotate in and out)实际的会话,这样它们就不会无限期地挂着最大虚拟内存。
我们确实是这样做,有一个maxLifeTime设置来旋转这些连接。
在真实场景中,fixed pool对连接风暴影响的保护是可见的。fixed pool下,数据库的瞬时active连接突增,数据库的idle链接数下降,但数据库的总连接数不变,请求响应耗时影响不大。如果把maximumPoolSize设置为比minimumIdle大的一个值,连接风暴会造成瞬间产生很多新会话,而新会话的连接是非常消耗资源的,这明显增加了请求的响应时间。
连接泄露案例
由于本人不是连接池的专家,这里只是把最近找到的连接泄露资料小小汇总下。
连接泄露有如下现象:
- “Connection is not available” exception。连接泄露,连接打满或者数据库因为active会话过度响应不过来了,新的请求会因为超过connectionTimeout时间而报错
- Growth of active connections。数据库监控可以明显看到活动会话上涨
- Application logs。应用日志也可以看到很多连接请求,包括活跃会话信息
- Database views and logs。pg_stat_activity可以看到所有的会话状态和具体的sql,以及在log中可以看到新连接认证登录信息
- HikariCP leak detection。需要打开leakDetectionThreshold,HikariCP可以检测链接泄露,这个参数默认是关闭的
对于定位连接泄露,应该
- 检查应用日志,特别是问题刚发生的时间点
- 合理的监控系统
- 善于debug、trace等hikariCP设置
- 设置leakDetectionThreshold参数
可能的原因:
- Misuse of streaming responses;
- Misuse of raw connections;
- Prolonged operations within @Transactional method (such as network invocation).
- 配置错误,参考
- vitual thread,参考