Spring Boot2.5 集成数据库连接池 HikariCP
摘要:浅谈连接池基本概念和工作原理、常见数据库连接池性能对比、HiKariCP速度为什么快和常见属性对比。最后给出一个Spring Boot整合HiKariCP的入门案例。
§工程环境
- JDK:1.8.0_231
- maven:3.6.1
- Apache Tomcat:9.0.46
- Spring Boot: 2.5.0
- mysql-connector-java:8.0.25
- mysql:8.0.25
- HikariCP:4.0.3
§数据库连接池介绍
一个普通的java程序,要查询数据库的数据,基本流程是这样的:

可以看到:进行一次查询,要进行很多次网络交互,这样的缺点是:
网络IO多
响应时间长,导致QPS降低
频繁创建连接和关闭连接,浪费数据库资源,影响服务器性能
因为TCP连接的创建开支十分昂贵,并且数据库所能承载的TCP并发连接数也有限制,针对这种场景,数据库连接池应运而生。数据库连接池是用于创建、管理和释放数据库连接的缓冲池技术,缓冲池中的连接可以被任何需要它们的线程使用。当一个线程需要用JDBC对一个数据库操作时,将从池中请求一个连接;当这个连接使用完毕后,将返回到连接池中,等待其它线程的调度。
这里用到了池化技术,如大家屡见不鲜的线程池、整数池、字符串池、对象池和Http 连接池等等,都是对这个思想的应用。池化技术的思想主要是通过复用对象,以减少每次获取资源时创建和释放所带来的资源消耗,提高资源利用率,这是典型的以空间换取时间的策略。
数据库连接池负责分配、管理、释放数据库连接,它允许应用服务重复使用数据库连接,而非重新建立。使用连接池之后,流程是这样的:

由此可见,数据库连接的创建和关闭连接均由连接池来实现。这样的机制有如下两个优点:
- 封装关于数据库访问的各种参数,实现统一管理
- 通过对数据库的连接池管理,减少网络开销并提升数据库性能
数据库连接池工作原理剖析
数据库连接池的工作原理主要由三部分组成,分别为
- 连接池的建立
- 连接池的管理
- 连接池的关闭
连接池的建立。应用初始化时,根据配置的最小连接数,在连接池将创建此数目的数据库连接放到池中,以便使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。
Java中提供了很多容器类可以方便的构建连接池,例如Vector、Stack等。

连接池的管理。连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是:当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户。 当客户释放数据库连接时,先判断池中的连接数是否超过了设置的最大连接数,如果超过就从连接池中删除该连接;否则,保留连接,等待再次使用。

连接池的关闭。应用程序关闭时,关闭连接池中所有连接,释放所有相关资源。

在Java这个自由开放的生态中,已经有非常多优秀的开源数据库连接池可以供大家选择,比如:DBCP、C3P0、Druid、HikariCP、tomcat-jdbc等。而在Spring Boot 2.x中,对数据源的选择也紧跟潮流,采用了目前性能最佳的HikariCP。接下来,我们就来具体聊聊HikariCP。
§Java常见数据库连接池性能比较
单从性能角度分析,性能从高到低依次是:HikariCP、druid、tomcat-jdbc、dbcp、c3p0。下图是HikariCP官网给出的性能对比:

从上图中可以直观的看出,Hikari 在 获取和释放 Connection 和 Statement 方法的 OPS 不是一般的高,那是相当的高,基本上是碾压其他连接池,这里就不一一点名了。除了 OPS 外,HikariCP 的稳定性也更好,性能毛刺更少。
§数据库连接池选型 Druid vs HikariCP性能对比
- 从功能角度考虑,Druid 功能更丰富,除具备连接池基本功能外,还支持sql级监控、扩展、SQL防注入等。最新版甚至有集群监控。两者的侧重点不一样。
- 从性能角度考虑,从数据处理速度角度来看,HikariCP确实更强,但Druid由阿里巴巴背书,可支持”双十一”等最严苛的使用场景,并且提供了强大的监控功能,在国内有不少用户。不过,Spring Boot 2.x已经使用HikariCP作为默认的数据库连接池,其优秀程度可见一斑。
- 从监控角度考虑,如果我们有像skywalking、prometheus等组件是可以将监控能力交给这些的,HikariCP也可以将metrics暴露出去。
HikariCP作为后起之秀,是目前最快的Java数据库连接池。
§HikariCP为什么这么快
HikariCP为什么这么快呢?是因为它在如下四个方面做了优化,以提升性能:
- 优化并精简字节码。使用Java字节码修改类库Javassist来生成委托实现动态代理,比JDK Proxy生成的字节码更少,精简了很多不必要的字节码。
- 使用自定义的无锁的、性能更好的并发集合类ConcurrentBag。
- 使用自定义的数组类型FastList替代ArrayList。FastList是List接口的精简实现。
- 优化代理和拦截器:减少代码,例如 HikariCP 的 Statement proxy 只有100行代码,只有 BoneCP 的十分之一;
下面是FastList源码:
/**
* ArrayList精简版的、没有列表检查的 FastList
*
* @author Brett Wooldridge
*/
public final class FastList<T> implements List<T>, RandomAccess, Serializable{
private static final long serialVersionUID = -4598088075242913858L;
private final Class<?> clazz;
private T[] elementData;
private int size;
/**
* 构建一个默认大小为32的列表。
* @param clazz the Class stored in the collection
*/
@SuppressWarnings("unchecked")
public FastList(Class<?> clazz) {
this.elementData = (T[]) Array.newInstance(clazz, 32);
this.clazz = clazz;
}
/**
* 构造具有指定大小的列表。
* @param clazz the Class stored in the collection
* @param capacity the initial size of the FastList
*/
@SuppressWarnings("unchecked")
public FastList(Class<?> clazz, int capacity) {
this.elementData = (T[]) Array.newInstance(clazz, capacity);
this.clazz = clazz;
}
@Override
public boolean add(T element) {
//给 list添加属性
//如果 size值小于 初始化的值
if (size < elementData.length) {
elementData[size++] = element;
} else {
// 溢出的代码
//elementData 原始32不够用 需要扩容
final int oldCapacity = elementData.length;
final int newCapacity = oldCapacity << 1;
@SuppressWarnings("unchecked")
//扩容集合
final T[] newElementData = (T[]) Array.newInstance(clazz, newCapacity);
//数组复制
System.arraycopy(elementData, 0, newElementData, 0, oldCapacity);
//属性赋值
newElementData[size++] = element;
elementData = newElementData;
}
return true;
}
/**
* 贴出ArrayList的get代码,来看看为什么 FastList 更快
* public E get(int index) {
* rangeCheck(index);
* return elementData(index);
* }
* ArrayList调用rangeCheck以检查角标范围,而FastList直接读取元素,节约时间
*/
@Override
public T get(int index) {
return elementData[index];
}
/**
* 这个是ArrayList的 remove()代码, FastList 少了检查范围和从头到尾的 检查元素动作,速度更快
* rangeCheck(index);
* modCount++;
* E oldValue = elementData(index);
*/
@Override
public boolean remove(Object element) {
for (int index = size - 1; index >= 0; index--) {
if (element == elementData[index]) {
final int numMoved = size - index - 1;
//如果角标不是最后一个 copy一个新的数组结构
if (numMoved > 0) {
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
}
//如果角标是最后面的 直接初始化为null
elementData[--size] = null;
return true;
}
}
return false;
}
§数据源配置详解
由于Spring Boot的自动化配置机制,大部分对于数据源的配置都可以通过配置参数的方式去改变。只有一些特殊情况,比如:更换默认数据源,多数据源共存等情况才需要去修改覆盖初始化的Bean内容。本节我们主要讲Hikari的配置,所以对于使用其他数据源或者多数据源的情况,在之后的教程中学习。
在Spring Boot自动化配置中,对于数据源的配置可以分为两类:
- 通用配置:以
spring.datasource.*的形式存在,主要是对一些即使使用不同数据源也都需要配置的一些常规内容。比如:数据库链接地址、用户名、密码等。这里就不做过多说明了,通常就这些配置:
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
温馨提示:driver-class-name用于指定JDBC驱动程序的类名,默认从jdbc url中自动探测。
com.mysql.jdbc.Driver 是 mysql-connector-java 5中的,com.mysql.cj.jdbc.Driver 是 mysql-connector-java 版本6以后的。
- 数据源连接池配置:以
spring.datasource.<数据源名称>.*的形式存在,比如:Hikari的配置参数就是spring.datasource.hikari.*形式。下面这个是我们最常用的几个配置项及对应说明:
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.idle-timeout=500000
spring.datasource.hikari.max-lifetime=540000
spring.datasource.hikari.connection-timeout=60000
spring.datasource.hikari.connection-test-query=SELECT 1
这些配置的含义:
spring.datasource.hikari.minimum-idle: 最小空闲连接,默认值10,小于0或大于maximum-pool-size,都会重置为maximum-pool-size。spring.datasource.hikari.maximum-pool-size: 最大连接数,小于等于0会被重置为默认值10;大于零小于1会被重置为minimum-idle的值spring.datasource.hikari.idle-timeout: 空闲连接超时时间,此属性控制允许连接在连接池中闲置的最长时间。默认值600000毫秒(10分钟),大于等于max-lifetime且max-lifetime>0,会被重置为0;不等于0且小于10秒,会被重置为10秒。此设置仅适用于maximumPoolSize-minimumIdle的连接。一旦连接池达到最小连接数,空闲连接将不会退出。在超时之前,连接永远不会退出。值为0意味着空闲连接永远不会从池中删除。允许的最小值是10000ms(10秒),默认值值是600000(10分钟)。
spring.datasource.hikari.max-lifetime: 连接最大存活时间,不等于0且小于30秒,会被重置为默认值30分钟.设置应该比mysql设置的超时时间短spring.datasource.hikari.connection-timeout: 连接超时时间:毫秒,小于250ms,否则被重置为默认值30秒spring.datasource.hikari.connection-test-query: 用于测试连接是否可用的查询语句
更多完整配置项可查看下表:
| name | 默认配置validate之后的值 | 构造器默认值 | validate重置 | 描述 |
|---|---|---|---|---|
| autoCommit | TRUE | TRUE | – | 自动提交从池中返回的连接 |
| connectionTimeout | 30000 | SECONDS.toMillis(30) = 30000 | 如果小于250毫秒,则被重置回30秒 | 等待来自池的连接的最大毫秒数 |
| idleTimeout | 600000 | MINUTES.toMillis(10) = 600000 | 如果idleTimeout+1秒>maxLifetime 且 maxLifetime>0,则会被重置为0(代表永远不会退出);如果idleTimeout!=0且小于10秒,则会被重置为10秒 | 连接允许在池中闲置的最长时间 |
| maxLifetime | 1800000 | MINUTES.toMillis(30) = 1800000 | 如果不等于0且小于30秒则会被重置回30分钟 | 池中连接最长生命周期 |
| connectionTestQuery | null | null | – | 如果您的驱动程序支持JDBC4,我们强烈建议您不要设置此属性 |
| minimumIdle | 10 | -1 | minIdle<0或者minIdle>maxPoolSize,则被重置为maxPoolSize | 池中维护的最小空闲连接数 |
| maximumPoolSize | 10 | -1 | 如果maxPoolSize小于1,则会被重置。当minIdle<=0被重置为DEFAULT_POOL_SIZE则为10;如果minIdle>0则重置为minIdle的值 | 池中最大连接数,包括闲置和使用中的连接 |
| metricRegistry | null | null | – | 该属性允许您指定一个 Codahale / Dropwizard MetricRegistry 的实例,供池使用以记录各种指标 |
| healthCheckRegistry | null | null | – | 该属性允许您指定池使用的Codahale / Dropwizard HealthCheckRegistry的实例来报告当前健康信息 |
| poolName | HikariPool-1 | null | – | 连接池的用户定义名称,主要出现在日志记录和JMX管理控制台中以识别池和池配置 |
| initializationFailTimeout | 1 | 1 | – | 如果池无法成功初始化连接,则此属性控制池是否将 fail fast |
| isolateInternalQueries | FALSE | FALSE | – | 是否在其自己的事务中隔离内部池查询,例如连接活动测试 |
| allowPoolSuspension | FALSE | FALSE | – | 控制池是否可以通过JMX暂停和恢复 |
| readOnly | FALSE | FALSE | – | 从池中获取的连接是否默认处于只读模式 |
| registerMbeans | FALSE | FALSE | – | 是否注册JMX管理Bean(MBeans) |
| catalog | null | driver default | – | 为支持 catalog 概念的数据库设置默认 catalog |
| connectionInitSql | null | null | – | 该属性设置一个SQL语句,在将每个新连接创建后,将其添加到池中之前执行该语句。 |
| driverClassName | null | null | – | HikariCP将尝试通过仅基于jdbcUrl的DriverManager解析驱动程序,但对于一些较旧的驱动程序,还必须指定driverClassName |
| transactionIsolation | null | null | – | 控制从池返回的连接的默认事务隔离级别 |
| validationTimeout | 5000 | SECONDS.toMillis(5) = 5000 | 如果小于250毫秒,则会被重置回5秒 | 连接将被测试活动的最大时间量 |
| leakDetectionThreshold | 0 | 0 | 如果大于0且不是单元测试,则进一步判断:(leakDetectionThreshold < SECONDS.toMillis(2) or (leakDetectionThreshold > maxLifetime && maxLifetime > 0),会被重置为0 . 即如果要生效则必须>0,而且不能小于2秒,而且当maxLifetime > 0时不能大于maxLifetime | 记录消息之前连接可能离开池的时间量,表示可能的连接泄漏 |
| dataSource | null | null | – | 这个属性允许你直接设置数据源的实例被池包装,而不是让HikariCP通过反射来构造它 |
| schema | null | driver default | – | 该属性为支持模式概念的数据库设置默认模式 |
| threadFactory | null | null | – | 此属性允许您设置将用于创建池使用的所有线程的java.util.concurrent.ThreadFactory的实例。 |
| scheduledExecutor | null | null | – | 此属性允许您设置将用于各种内部计划任务的java.util.concurrent.ScheduledExecutorService实例 |
§数据源配置案例
数据库连接池properties文件配置信息:
###数据源配置###
#默认就是hikari,可缺省
#spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mysql?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root
#默认30000ms,即30s
#spring.datasource.hikari.connection-timeout=30000
#存活时间,默认600000ms,即10min
spring.datasource.hikari.idle-timeout=600000
#连接池的最大尺寸(闲置连接+正在使用的连接),默认10
spring.datasource.hikari.maximum-pool-size=200
#最小空闲连接数,默认10
spring.datasource.hikari.minimum-idle=50
spring.datasource.hikari.pool-name=私有连接池
HikariDataSource在应用启动后,第一次数据库交互的时候加载连接池信息,这就是因为Spring Boot 2.x连接数据库用到了懒加载。
§Reference
Spring Boot2.5 集成数据库连接池 HikariCP的更多相关文章
- Spring Boot 数据库连接池 HikariCP
简介 HikariCP 来源于日语,「光」的意思,意味着它很快!可靠的数据源,spring boot2.0 已经将 HikariCP 做为了默认的数据源链接池. 官网详细地说明了HikariCP所做的 ...
- Spring Boot [使用 Druid 数据库连接池]
导读 最近一段时间比较忙,以至于很久没有更新Spring Boot系列文章,恰好最近用到Druid, 就将Spring Boot 使用 Druid作为数据源做一个简单的介绍. Druid介绍: Dru ...
- spring boot:配置druid数据库连接池(开启sql防火墙/使用log4j2做异步日志/spring boot 2.3.2)
一,druid数据库连接池的功能? 1,Druid是阿里巴巴开发的号称为监控而生的数据库连接池 它的优点包括: 可以监控数据库访问性能 SQL执行日志 SQL防火墙 2,druid的官方站: http ...
- 性能超过DRUID的最强数据库连接池——HikariCP相关配置及简单示例
在配置application.yml时,对hikari的配置会有这样一个字段validationQuery. validationQuery是用来验证数据库连接的查询语句,这个查询语句必须是至少返回一 ...
- SpringBoot 集成数据库连接池Druid
1.pom.xml依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>druid< ...
- spring boot2.x集成spring security5与druid1.1.13(一)
版本: spring boot 2.1.2.RELEASE druid-spring-boot-starter 1.1.13 步骤: 一.maven ...
- Spring Boot2+Resilience4j实现容错之Bulkhead
Resilience4j是一个轻量级.易于使用的容错库,其灵感来自Netflix Hystrix,但专为Java 8和函数式编程设计.轻量级,因为库只使用Vavr,它没有任何其他外部库依赖项.相比之下 ...
- 21Spring_JdbcTemplatem模板工具类的使用——配置文件(连接三种数据库连接池)
上一篇文章提到过DriverManagerDataSource只是Spring内置的数据库连接池,我们可选的方案还有c3p0数据库连接池以及DBCP数据库连接池. 所以这篇文章讲一下上面三种数据库连接 ...
- 搭建spring工程配置数据源连接池
Spring作为一个优秀的开源框架,越来越为大家所熟知,前段时间用搭了个spring工程来管理数据库连接池,没有借助Eclipse纯手工搭建,网上此类文章不多,这里给大家分享一下,也作为一个手记. 工 ...
- Druid数据库连接池使用体验
写在前面 在实际工作中我们我们使用较多的则是Spring默认的HikariDataSource数据库连接池,但是它无法提供可视化监控SQL这一能力,而这在很多场景下往往又是我们需要的功能,因此今天来学 ...
随机推荐
- vue-element-admin安装趟坑
1.下载源码 2.执行 npm install --registry=https://registry.npm.taobao.org 如果遇到"git ls-remote -h -t&quo ...
- python实现批量自动访问站点URL并获取内容,自动模拟打开电脑端及移动端URL访问站点,打开URL页面获取页面内容
问题描述:假设目前有多个网站URL,需要检查各站点keyword,description是否正常设置,如果人工逐个打开URL访问比较耗时,故采用python模拟电脑端和移动端自动打开网站URL访问,并 ...
- C# USB 摄像头 OpenCV 视频picBox呈现,抓拍图像保存呈现。没有注释版本。
1.winform 应用程序,两个picturebox空间,一个用于视频呈现,一个用于抓拍呈现. 2.引用包OpenCvSharp4.OpenCvSharp4.Extensions.OpenCvSha ...
- 【CIM信息整合】关于三维建筑模型
还是无暇细细检索并总结列出有逻辑的明确表述,以下很多地方都是人云亦云的复制,自己也没太搞清 1.5 三维建筑模型 CIM中三维建筑模型主要表达建(构)筑物的空间位置.几何形态及外观效果等. 在建筑相关 ...
- wordpress设置自定义字体
wordpress设置自定义字体: 失败的操作过程: 写在最前:试了一天多的引用字体,方法包括但不限于: 下载.ttf..otf格式字体,转化为wotf .wotf2格式,挂在github仓库用CDN ...
- 【离线地图】地图瓦片css复杂滤镜线段绘制
需求: 目前已经对地图瓦片做了复杂滤镜的黑夜展示,现在又要在这个图片上绘制新的线段等内容,且不能被这个复杂滤镜影响,变成奇奇怪怪的颜色. 同时因为框架限制,只能在这个img上绘制 思考: 1.既然不想 ...
- 使用df命令
1.使用df命令,查看整体的磁盘使用情况 df命令是用来查看硬盘的挂载点,以及对应的硬盘容量信息.包括硬盘的总大小,已经使用的大小,剩余大小.以及使用的空间占有的百分比等. 最常用的命令格式就是: 1 ...
- 实现领域驱动设计 - 使用ABP框架 - 聚合
这是本指南的关键部分.我们将通过实例介绍和解释一些明确的规则.在实现领域驱动设计时,您可以遵循这些规则并将其应用到您的解决方案中 领域案例 这些例子将使用GitHub中使用的一些概念,比如Issue, ...
- Python+硅基流动API实现小说转有声读物
一.注册硅基流动账号获取文本转语音api 1.注册登录硅基流动 注册.登录硅基流动 查看apikey 查看赠送的免费额度 点击文档中心 2.查看文本转语音api 查看文本转语音api 查看api使用指 ...
- Sql语句:数据操作
数据操作,核心是:增删改查. 其中查与增删改不同,要返回数据集,其他的只要知道是否修改成功即可,所以一般调用时,返回值不同,这点要注意. 一.查询: select sname,sdept,sage f ...