HikariCP数据库连接池

介绍

HikariCP 号称是 史上性能最好 的数据库连接池,从15年推出以来,已经经受了广大应用市场的考验,并且成功地被SpringBoot2.0作为默认数据库连接池进行推广,在可靠性上面是值得信任的。其次借助于其代码量少,占用cpu和内存量小的优点,使得它的执行率非常高。最后,Spring配置HikariCP和druid基本没什么区别,迁移过来非常方便,这些都是为什么HikariCP目前如此受欢迎的原因。

优化

Hikari相比起其它连接池的性能高了非常多,那么,这是怎么做到的呢?通过查看HikariCP官网介绍,对于HikariCP所做优化总结如下:

  1. 字节码精简 :优化代码,编译后的字节码量极少,使得CPU缓存可以加载更多的程序代码;
    HikariCP在优化并精简字节码上也下了功夫,使用第三方的Java字节码修改类库Javassist来生成委托实现动态代理.动态代理的实现在ProxyFactory类,速度更快,相比于JDK Proxy生成的字节码更少,精简了很多不必要的字节码。

  2. 优化代理和拦截器:减少代码,例如HikariCP的Statement proxy只有100行代码,只有BoneCP的十分之一;

  3. 自定义数组类型(FastStatementList)代替ArrayList:避免ArrayList每次get()都要进行range check,避免调用remove()时的从头到尾的扫描(由于连接的特点是后获取连接的先释放);

  4. 自定义集合类型(ConcurrentBag):提高并发读写的效率;

  5. 关于Connection的操作:另外在Java代码中,很多都是在使用完之后直接关闭连接,以前都是从头到尾遍历,来关闭对应的Connection,而HikariCP则是从尾部对Connection集合进行扫描,整体上来说,从尾部开始的性能更好一些。

对比

依赖

<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>3.1.0</version>
</dependency>

或

<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>4.0.3</version>
</dependency>

配置

HikariCP 所有时间相关的参数单位都为 ms。

详细的配置说明可参考官网文档:https://github.com/brettwooldridge/HikariCP

基本配置

参数 默认值 描述
dataSourceClassName none 驱动里面数据源的类名称;不支持 XA数据源,各数据源对应的数据源类名可参见 ”2、数据源类名“
jdbcUrl none 连接 url;该参数与 dataSourceClassName 设置一个即可
username none 用户名
password none 密码

常用配置

参数 默认值 描述
autoCommit true 连接返回连接池时,是否自动提交事务
connectionTimeout 30000 从连接池获取连接的最大超时时间
idleTimeout 60000 空闲连接存活的最大时间,当空闲连接数>minimumIdle 且 连接的空闲状态时间>idleTimeout 时,将把该连接从连接池中删除;只有当 minimumIdle < maximumPoolSize 时,该设置才生效;0 表示永不超时
keepaliveTime 0 保持空闲连接可用的检测频率,单位:ms;0 表示不检测
maxLifetime 1800000 连接存活的最大时间;0 表示没有限制
connectionTestQuery none 连接检测的查询语句;如果驱动支持 JDBC 4,强烈建议不要设置此参数
minimumIdle same as maximumPoolSize 最小空闲连接数;为了提高性能,建议不要设置此参数,使连接池为固定大小
maximumPoolSize 10 最大连接数
metricRegistry none 该参数仅通过编程配置或 IoC 容器可用;该参数用于指定池使用的 Codahale/Dropwizard MetricRegistry实例来记录各种指标。
healthCheckRegistry none 该参数仅通过编程配置或 IoC 容器可用;该参数用于指定池使用的 Codahale/Dropwizard HealthCheckRegistry实例来记录健康信息。
poolName auto-generated 连接池名称

不常用配置

参数 默认值 描述
initializationFailTimeout 1 启动连接池时不能成功初始化连接,是否快速失败。>0 时,会尝试获取连接。如果获取时间超过指定时长(connectionTimeout + initializationFailTimeout),不会开启连接池,并抛出异常。=0 时,会尝试获取并验证连接。如果获取成功但验证失败则不开启池,但是如果获取失败还是会开启池。<0 时,直接启动连接池,不进行初始化连接尝试。
isolateInternalQueries false 是否在事务中隔离 HikariCP 自己的查询;autoCommit 为 false 时,该设置生效
allowPoolSuspension false 是否允许通过 JMX 挂起和恢复连接池;挂起时获取连接不会超时,知道连接池恢复
readOnly false 连接是否只读
registerMbeans false 是否开启 JMX
catalog driver default 默认的数据库 catalog
connectionInitSql none 连接池初始化后执行的 SQL
driverClassName none 驱动类名;HikariCP 会尝试通过 jdbcUrl 来确定 driverClassName,但对一些老的驱动必须指定 driverClassName;可以忽略该配置除非报 “the driver was not found” 的错误。
transactionIsolation driver default 默认的事务隔离级别
validationTimeout 5000 连接检测的超时时间;必须大于 connectionTimeout,最小允许的值为 250
leakDetectionThreshold 0 连接可以被借出多久;超过该时间将打印连接可能泄露的日志,最小允许的值为 2000,单位 ms
dataSource none 指定数据源;此参数仅通过编程配置或 IoC 容器可用,当设置此参数,dataSourceClassName 和所有 DataSource- 相关的属性将被忽略。
schema driver default 默认的数据库 schema
threadFactory none 指定连接池用于创建线程的 java.util.concurrent.ThreadFactory 实例;此参数仅通过编程配置或 IoC 容器可用
scheduledExecutor none 指定连接池用于执行定时任务的 java.util.concurrent.ScheduledExecutorService 实例;此参数仅通过编程配置或 IoC 容器可用

例子

package com.abc.demo.general.dbpool;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class HikariCPCase {
    private static Logger logger = LoggerFactory.getLogger(HikariCPCase.class);

    /**
     * 使用 dataSourceClassName
     */
    @Test
    public void test1() {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setPoolName("HikariCP 连接池");
        hikariConfig.setDataSourceClassName("oracle.jdbc.pool.OracleDataSource");
        hikariConfig.addDataSourceProperty("user", "test");
        hikariConfig.addDataSourceProperty("password", "123456");
        hikariConfig.addDataSourceProperty("url", "jdbc:oracle:thin:@10.49.196.10:1521:test");
        hikariConfig.setMaximumPoolSize(15);

        HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);
        Connection connection = null;
        try {
            connection = hikariDataSource.getConnection();
            Statement st = connection.createStatement();
            ResultSet rs = st.executeQuery("select * from v$version");
            if (rs.next()) {
                logger.info(rs.getString(1));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(connection);
        }

        //实际使用中一般是在应用启动时初始化数据源,应用从数据源中获取连接;并不会关闭数据源。
        hikariDataSource.close();
    }

    /**
     * 使用jdbc-url
     */
    @Test
    public void test2() {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setPoolName("HikariCP 连接池");
        hikariConfig.setDriverClassName("com.mysql.cj.jdbc.Driver");
        hikariConfig.setJdbcUrl("jdbc:mysql://10.49.196.11:3306/mydb?useUnicode=true&characterEncoding=UTF-8");
        hikariConfig.setUsername("root");
        hikariConfig.setPassword("123456");
        hikariConfig.setMinimumIdle(2);
        hikariConfig.setMaximumPoolSize(10);

        HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);
        Connection connection = null;
        try {
            connection = hikariDataSource.getConnection();
            Statement st = connection.createStatement();
            ResultSet rs = st.executeQuery("select version()");
            if (rs.next()) {
                logger.info(rs.getString(1));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(connection);
        }

        //实际使用中一般是在应用启动时初始化数据源,应用从数据源中获取连接;并不会关闭数据源。
        hikariDataSource.close();
    }

    private static void close(Connection connection) {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

参考:
https://blog.csdn.net/weixin_59383491/article/details/131437532

https://www.cnblogs.com/wuyongyin/p/15560017.html


原文出处:/show_1GWCn1prBwU.html