摘要:浅谈连接池基本概念和工作原理、常见数据库连接池性能对比、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程序,要查询数据库的数据,基本流程是这样的:

  可以看到:进行一次查询,要进行很多次网络交互,这样的缺点是:

  1. 网络IO多

  2. 响应时间长,导致QPS降低

  3. 频繁创建连接和关闭连接,浪费数据库资源,影响服务器性能

  因为TCP连接的创建开支十分昂贵,并且数据库所能承载的TCP并发连接数也有限制,针对这种场景,数据库连接池应运而生。数据库连接池是用于创建、管理和释放数据库连接的缓冲池技术,缓冲池中的连接可以被任何需要它们的线程使用。当一个线程需要用JDBC对一个数据库操作时,将从池中请求一个连接;当这个连接使用完毕后,将返回到连接池中,等待其它线程的调度。

  这里用到了池化技术,如大家屡见不鲜的线程池、整数池、字符串池、对象池和Http 连接池等等,都是对这个思想的应用。池化技术的思想主要是通过复用对象,以减少每次获取资源时创建和释放所带来的资源消耗,提高资源利用率,这是典型的以空间换取时间的策略。

  数据库连接池负责分配、管理、释放数据库连接,它允许应用服务重复使用数据库连接,而非重新建立。使用连接池之后,流程是这样的:

  由此可见,数据库连接的创建和关闭连接均由连接池来实现。这样的机制有如下两个优点:

  1. 封装关于数据库访问的各种参数,实现统一管理
  2. 通过对数据库的连接池管理,减少网络开销并提升数据库性能

数据库连接池工作原理剖析

  数据库连接池的工作原理主要由三部分组成,分别为

  • 连接池的建立
  • 连接池的管理
  • 连接池的关闭
  1. 连接池的建立。应用初始化时,根据配置的最小连接数,在连接池将创建此数目的数据库连接放到池中,以便使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。

    Java中提供了很多容器类可以方便的构建连接池,例如Vector、Stack等。

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

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

  在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为什么这么快呢?是因为它在如下四个方面做了优化,以提升性能:

  1. 优化并精简字节码。使用Java字节码修改类库Javassist来生成委托实现动态代理,比JDK Proxy生成的字节码更少,精简了很多不必要的字节码。
  2. 使用自定义的无锁的、性能更好的并发集合类ConcurrentBag。
  3. 使用自定义的数组类型FastList替代ArrayList。FastList是List接口的精简实现。
  4. 优化代理和拦截器:减少代码,例如 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的更多相关文章

  1. Spring Boot 数据库连接池 HikariCP

    简介 HikariCP 来源于日语,「光」的意思,意味着它很快!可靠的数据源,spring boot2.0 已经将 HikariCP 做为了默认的数据源链接池. 官网详细地说明了HikariCP所做的 ...

  2. Spring Boot [使用 Druid 数据库连接池]

    导读 最近一段时间比较忙,以至于很久没有更新Spring Boot系列文章,恰好最近用到Druid, 就将Spring Boot 使用 Druid作为数据源做一个简单的介绍. Druid介绍: Dru ...

  3. spring boot:配置druid数据库连接池(开启sql防火墙/使用log4j2做异步日志/spring boot 2.3.2)

    一,druid数据库连接池的功能? 1,Druid是阿里巴巴开发的号称为监控而生的数据库连接池 它的优点包括: 可以监控数据库访问性能 SQL执行日志 SQL防火墙 2,druid的官方站: http ...

  4. 性能超过DRUID的最强数据库连接池——HikariCP相关配置及简单示例

    在配置application.yml时,对hikari的配置会有这样一个字段validationQuery. validationQuery是用来验证数据库连接的查询语句,这个查询语句必须是至少返回一 ...

  5. SpringBoot 集成数据库连接池Druid

    1.pom.xml依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>druid< ...

  6. spring boot2.x集成spring security5与druid1.1.13(一)

    版本:         spring boot 2.1.2.RELEASE         druid-spring-boot-starter 1.1.13 步骤:        一.maven    ...

  7. Spring Boot2+Resilience4j实现容错之Bulkhead

    Resilience4j是一个轻量级.易于使用的容错库,其灵感来自Netflix Hystrix,但专为Java 8和函数式编程设计.轻量级,因为库只使用Vavr,它没有任何其他外部库依赖项.相比之下 ...

  8. 21Spring_JdbcTemplatem模板工具类的使用——配置文件(连接三种数据库连接池)

    上一篇文章提到过DriverManagerDataSource只是Spring内置的数据库连接池,我们可选的方案还有c3p0数据库连接池以及DBCP数据库连接池. 所以这篇文章讲一下上面三种数据库连接 ...

  9. 搭建spring工程配置数据源连接池

    Spring作为一个优秀的开源框架,越来越为大家所熟知,前段时间用搭了个spring工程来管理数据库连接池,没有借助Eclipse纯手工搭建,网上此类文章不多,这里给大家分享一下,也作为一个手记. 工 ...

  10. Druid数据库连接池使用体验

    写在前面 在实际工作中我们我们使用较多的则是Spring默认的HikariDataSource数据库连接池,但是它无法提供可视化监控SQL这一能力,而这在很多场景下往往又是我们需要的功能,因此今天来学 ...

随机推荐

  1. vue-element-admin安装趟坑

    1.下载源码 2.执行 npm install --registry=https://registry.npm.taobao.org 如果遇到"git ls-remote -h -t&quo ...

  2. python实现批量自动访问站点URL并获取内容,自动模拟打开电脑端及移动端URL访问站点,打开URL页面获取页面内容

    问题描述:假设目前有多个网站URL,需要检查各站点keyword,description是否正常设置,如果人工逐个打开URL访问比较耗时,故采用python模拟电脑端和移动端自动打开网站URL访问,并 ...

  3. C# USB 摄像头 OpenCV 视频picBox呈现,抓拍图像保存呈现。没有注释版本。

    1.winform 应用程序,两个picturebox空间,一个用于视频呈现,一个用于抓拍呈现. 2.引用包OpenCvSharp4.OpenCvSharp4.Extensions.OpenCvSha ...

  4. 【CIM信息整合】关于三维建筑模型

    还是无暇细细检索并总结列出有逻辑的明确表述,以下很多地方都是人云亦云的复制,自己也没太搞清 1.5 三维建筑模型 CIM中三维建筑模型主要表达建(构)筑物的空间位置.几何形态及外观效果等. 在建筑相关 ...

  5. wordpress设置自定义字体

    wordpress设置自定义字体: 失败的操作过程: 写在最前:试了一天多的引用字体,方法包括但不限于: 下载.ttf..otf格式字体,转化为wotf .wotf2格式,挂在github仓库用CDN ...

  6. 【离线地图】地图瓦片css复杂滤镜线段绘制

    需求: 目前已经对地图瓦片做了复杂滤镜的黑夜展示,现在又要在这个图片上绘制新的线段等内容,且不能被这个复杂滤镜影响,变成奇奇怪怪的颜色. 同时因为框架限制,只能在这个img上绘制 思考: 1.既然不想 ...

  7. 使用df命令

    1.使用df命令,查看整体的磁盘使用情况 df命令是用来查看硬盘的挂载点,以及对应的硬盘容量信息.包括硬盘的总大小,已经使用的大小,剩余大小.以及使用的空间占有的百分比等. 最常用的命令格式就是: 1 ...

  8. 实现领域驱动设计 - 使用ABP框架 - 聚合

    这是本指南的关键部分.我们将通过实例介绍和解释一些明确的规则.在实现领域驱动设计时,您可以遵循这些规则并将其应用到您的解决方案中 领域案例 这些例子将使用GitHub中使用的一些概念,比如Issue, ...

  9. Python+硅基流动API实现小说转有声读物

    一.注册硅基流动账号获取文本转语音api 1.注册登录硅基流动 注册.登录硅基流动 查看apikey 查看赠送的免费额度 点击文档中心 2.查看文本转语音api 查看文本转语音api 查看api使用指 ...

  10. Sql语句:数据操作

    数据操作,核心是:增删改查. 其中查与增删改不同,要返回数据集,其他的只要知道是否修改成功即可,所以一般调用时,返回值不同,这点要注意. 一.查询: select sname,sdept,sage f ...