浅析HikariCP配置与集成
HikariCP 是一个高性能的 JDBC 连接池组件,号称性能最好的后起之秀,是一个基于BoneCP做了不少的改进和优化的高性能JDBC连接池。本文将从配置和SpringBoot集成进行分析
1. HikariCP配置
1.1 HikariConfig
HikariConfig的成员变量涵盖了所有Hikari连接池的配置,如下:
java
//运行时,可以修改的配置。
//从连接池获取连接的超时时间,默认30s
private volatile long connectionTimeout;
//检验连接是否有效的超时时间,默认5s
private volatile long validationTimeout;
//连接空闲时间,当最小连接数 < 最大连接数生效。默认10min
private volatile long idleTimeout;
//连接泄漏检测时长,默认0不开启,最大值不能超过maxLifetime
private volatile long leakDetectionThreshold;
//连接最大存活时间,默认30min。需要小于数据库wait_timeout,超过这个时间未使用连接都会关闭。
private volatile long maxLifetime;
//最大连接数,默认10
private volatile int maxPoolSize;
//最小连接数,默认10
private volatile int minIdle;
//运行时,无法修改的配置
//初始化检测与数据库连接是否ok的超时时间,默认1ms,为0代表不做初始化检查。
private long initializationFailTimeout;
//创建连接成功后,在放入连接池之前,执行的sql
private String connectionInitSql;
//检测连接是否可用的sql,如果为空使用ping,否则使用这个sql
private String connectionTestQuery;
//是否自动提交,默认true
private Boolean isAutoCommit;
//是否只读,默认false
private Boolean isReadOnly;
//是否注册MBean
private Boolean isRegisterMbeans;
//是否允许连接池挂起,默认否
private Boolean isAllowPoolSuspension;
1.2 参数检验
检验方法在HikariConfig中的validateNumerics方法。
1)如果maxLifetime小于30s,则会用默认的时长:30min
java
if (maxLifetime != 0 && maxLifetime < SECONDS.toMillis(30)) {
LOGGER.warn("{} - maxLifetime is less than 30000ms, setting to default {}ms.", poolName, MAX_LIFETIME);
maxLifetime = MAX_LIFETIME;
}
2)如果maxLifetime大于0,idleTimeout + 1s 大于maxLifetime,则idleTimeout设置为0,表示不生效
java
if (idleTimeout + SECONDS.toMillis(1) > maxLifetime && maxLifetime > 0) {
LOGGER.warn("{} - idleTimeout is close to or more than maxLifetime, disabling it.", poolName);
idleTimeout = 0;
}
3)如果idleTimeout大于0,且小于10s,则设置成默认时长:10min
java
if (idleTimeout != 0 && idleTimeout < SECONDS.toMillis(10)) {
LOGGER.warn("{} - idleTimeout is less than 10000ms, setting to default {}ms.", poolName, IDLE_TIMEOUT);
idleTimeout = IDLE_TIMEOUT;
}
4)如果获取连接超时时长小于250ms,则设置为默认超时时长:30s
java
if (connectionTimeout < 250) {
LOGGER.warn("{} - connectionTimeout is less than 250ms, setting to {}ms.", poolName, CONNECTION_TIMEOUT);
connectionTimeout = CONNECTION_TIMEOUT;
}
5)如果验证时长小于250ms,则设置为默认验证超时时长:5s
java
if (validationTimeout < 250) {
LOGGER.warn("{} - validationTimeout is less than 250ms, setting to {}ms.", poolName, VALIDATION_TIMEOUT);
validationTimeout = VALIDATION_TIMEOUT;
}
6)如果最大连接数小于1,如果最小连接数没设置,则设置成默认10,否则设置成最小连接数。
java
if (maxPoolSize < 1) {
maxPoolSize = (minIdle <= 0) ? DEFAULT_POOL_SIZE : minIdle;
}
7)如果最小连接数设置,且大于最大连接数,则设置为最大连接数
java
if (minIdle < 0 || minIdle > maxPoolSize) {
minIdle = maxPoolSize;
}
1.3 参数用途
- validationTimeout:用于Connection接口的setNetworkTimeout方法,底层与应该是设置SocketTimeout的时长。另外如果设置连接查询参数,会调用Connection的isValid的接口,参数都是用validationTimeout,且最小不会少于1s。
java
//PoolBase#isConnectionAlive
setNetworkTimeout(connection, validationTimeout);
final int validationSeconds = (int) Math.max(1000L, validationTimeout) / 1000;
if (isUseJdbc4Validation) {
return connection.isValid(validationSeconds);
}
-
connectionTimeout:1)会被设置成DataSource的setLoginTimeout的参数。2)HikariPool对象池中获取对象的超时时长。
-
idleTimeout:如果对象处理空闲(并且要大于minimumIdle的数量),就会把连接给关闭掉。注意:即使设置了该参数,连接池对象也不会少于minimumIdle的数量。
-
maxLifeTime:连接最长存活的时间,不论对象是处于什么状态。在创建连接对象时,通过延迟任务处理。
-
minimumIdle:最少空闲连接数。空闲的连接有一定可用的数量,会在很多地方进行填充。
-
maxPoolSize:连接池最大对象数量。包括所有状态。
2. Spring Boot集成
2.1 数据源是什么
数据源(DataSource是用于连接物理的数据来源(如数据库),用于替代JDBC的DriverManager,使用URL和凭据(credentials)来建立数据库连接。
数据源可用于获取:
- 标准connection对象。
- 可被连接池管理的连接。
- 可被分布式、支持事务的连接池管理的连接。
2.2 框架如何使用数据源
- 以Mybatis为例。在MybatisAutoConfiguration中,会自动创建一个SqlSessionFactory,其中创建该方法直接依赖了DataSource 数据源
java
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
//....
return factory.getObject();
}
2.3 Spring Boot 如何集成
- 在DataSourceAutoConfiguration中,默认会按顺序加载Hikari、Tomcat、Dbcp2(只会加载一个)。
java
@Bean
@ConfigurationProperties(
prefix = "spring.datasource.hikari"
)
public HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = (HikariDataSource)DataSourceConfiguration.createDataSource(properties, HikariDataSource.class);
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
也可以在业务中显示的创建DataSource
java
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
HikariDataSource继承了HikariConfig实现了DataSource接口,所以@ConfigurationProperties会把配置直接注入进去。
使用DataSource的入口是从getConnection()获取一个连接开始。
该方法会判断一个连接池对象是否创建,如果没有创建则进行初始化。是一个典型的懒加载单例模式,pool对象用volatile关键字修饰。
java
public Connection getConnection() throws SQLException
{
//...
HikariPool result = pool;
if (result == null) {
synchronized (this) {
result = pool;
if (result == null) {
validate();
LOGGER.info("{} - Starting...", getPoolName());
try {
pool = result = new HikariPool(this);
this.seal();
}catch (PoolInitializationException pie) {
//...
}
LOGGER.info("{} - Start completed.", getPoolName());
}
}
}
return result.getConnection();
}