Java 连接池(HikariCP 的配置详解)

什么是连接池?

大家都知道TCP连接是需要经过三次握手才能建立连接,也就是说建立一次连接是一个耗时的动作, 对数据库来说是一个很耗性能的操作; 怎么办呢?

那就是连接池,连接池就是把建立好的连接保存起来,放在池里面,等有需要的时候再从池里拿 出来,不需要的时候,再重新放进去,这样做的目的就是减少与数据库频繁的建立连接,从而提高 性能!

特别提醒下:

SpringBoot 2.0 开始推 HikariCP ,将默认的数据库连接池从 tomcat jdbc pool 改为了 hikari !

具说: HikariCP 在性能和并发方面确实表现不俗(号称最快的连接池)。

不管怎样,我现在全部用的是HikariCP

连接池分类

  • HikariCP
  • C3P0
  • BoneCP
  • Druid

Druid 是阿里巴巴开源的,但是目前使用最广泛的是HikariCP,因为它最快!所以现在好多公司 都开始使用它了!

如果你使用 druid, 请引入 druid jar

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.1.9</version>
</dependency>

如果你使用 HikariCP,请引入 HikariCP jar

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

HikariCP 的配置详解

name    描述    构造器默认值    默认配置validate之后的值    validate重置
autoCommit    自动提交从池中返回的连接    true    true    -
connectionTimeout    等待来自池的连接的最大毫秒数    SECONDS.toMillis(30) = 30000    30000    如果小于250毫秒,则被重置回30秒
idleTimeout    连接允许在池中闲置的最长时间    MINUTES.toMillis(10) = 600000    600000    如果idleTimeout+1秒>maxLifetime 且 maxLifetime>0,则会被重置为0(代表永远不会退出);如果idleTimeout!=0且小于10秒,则会被重置为10秒
maxLifetime    池中连接最长生命周期    MINUTES.toMillis(30) = 1800000    1800000    如果不等于0且小于30秒则会被重置回30分钟
connectionTestQuery    如果您的驱动程序支持JDBC4,我们强烈建议您不要设置此属性    null    null    -
minimumIdle    池中维护的最小空闲连接数    -1    10    minIdle<0或者minIdle>maxPoolSize,则被重置为maxPoolSize
maximumPoolSize    池中最大连接数,包括闲置和使用中的连接    -1    10    如果maxPoolSize小于1,则会被重置。当minIdle<=0被重置为DEFAULT_POOL_SIZE则为10;如果minIdle>0则重置为minIdle的值
metricRegistry    该属性允许您指定一个 Codahale / Dropwizard MetricRegistry 的实例,供池使用以记录各种指标    null    null    -
healthCheckRegistry    该属性允许您指定池使用的Codahale / Dropwizard HealthCheckRegistry的实例来报告当前健康信息    null    null    -
poolName    连接池的用户定义名称,主要出现在日志记录和JMX管理控制台中以识别池和池配置    null    HikariPool-1    -
initializationFailTimeout    如果池无法成功初始化连接,则此属性控制池是否将 fail fast    1    1    -
isolateInternalQueries    是否在其自己的事务中隔离内部池查询,例如连接活动测试    false    false    -
allowPoolSuspension    控制池是否可以通过JMX暂停和恢复    false    false    -
readOnly    从池中获取的连接是否默认处于只读模式    false    false    -
registerMbeans    是否注册JMX管理Bean(MBeans)    false    false    -
catalog    为支持 catalog 概念的数据库设置默认 catalog    driver default    null    -
connectionInitSql    该属性设置一个SQL语句,在将每个新连接创建后,将其添加到池中之前执行该语句。    null    null    -
driverClassName    HikariCP将尝试通过仅基于jdbcUrl的DriverManager解析驱动程序,但对于一些较旧的驱动程序,还必须指定driverClassName    null    null    -
transactionIsolation    控制从池返回的连接的默认事务隔离级别    null    null    -
validationTimeout    连接将被测试活动的最大时间量    SECONDS.toMillis(5) = 5000    5000    如果小于250毫秒,则会被重置回5秒
leakDetectionThreshold    记录消息之前连接可能离开池的时间量,表示可能的连接泄漏    0    0    如果大于0且不是单元测试,则进一步判断:(leakDetectionThreshold < SECONDS.toMillis(2) or (leakDetectionThreshold > maxLifetime && maxLifetime > 0),会被重置为0 . 即如果要生效则必须>0,而且不能小于2秒,而且当maxLifetime > 0时不能大于maxLifetime
dataSource    这个属性允许你直接设置数据源的实例被池包装,而不是让HikariCP通过反射来构造它    null    null    -
schema    该属性为支持模式概念的数据库设置默认模式    driver default    null    -
threadFactory    此属性允许您设置将用于创建池使用的所有线程的java.util.concurrent.ThreadFactory的实例。    null    null    -
scheduledExecutor    此属性允许您设置将用于各种内部计划任务的java.util.concurrent.ScheduledExecutorService实例    null    null    -

连接池初始化

import com.alibaba.druid.pool.DruidDataSource;
import com.pangugle.framework.conf.MyConfiguration;
import com.pangugle.framework.log.Log;
import com.pangugle.framework.log.LogFactory;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

public class MyDataSourceFactory {

	private static Log LOG = LogFactory.getLog(MyDataSourceFactory.class);
	private static MyConfiguration conf = MyConfiguration.getInstance();

	public static DataSource create(String name) {
		return getHikariCP(name);
	}

	public static DataSource getDruid(String name) {
		try {
			// jdbc.master.driver_class
			// jdbc.master.url
			// jdbc.master.username
			// jdbc.master.password
			String driver = conf.getString(name + ".driver_class", "com.mysql.jdbc.Driver");
			String jdbcUrl = conf.getString(name + ".url");
			String username = conf.getString(name + ".username");
			String password = conf.getString(name + ".password");

			LOG.info("jdbcurl = " + jdbcUrl);

			Class.forName(driver);

			int maxActive = MyDBConfigManager.DEFAULT_JDBC_MAX_ACTIVE;


			DruidDataSource pool = new DruidDataSource();
			pool.setUrl(jdbcUrl);
			pool.setUsername(username);
			pool.setPassword(password);
			pool.setInitialSize(MyDBConfigManager.DEFAULT_JDBC_INIT_SIZE); // 初始化连接
			pool.setMaxActive(maxActive); // 最大连接
			pool.setMinIdle(MyDBConfigManager.DEFAULT_JDBC_MIN_IDLE); // 最小连接
			pool.setTimeBetweenEvictionRunsMillis(MyDBConfigManager.DEFAULT_JDBC_TIME_BETWEEN_EVICTION_RUN); // 空闲检测时间
			pool.setTestOnBorrow(true);
			pool.setTestOnReturn(false);
			pool.setTestWhileIdle(true);
			pool.setRemoveAbandoned(false); // 设置是否开启连接租期
			pool.setValidationQuery("select 1");

			return pool;
		} catch (ClassNotFoundException e) {
			LOG.error("init druid datasource error:", e);
		}
		return null;
	}

	private static DataSource getHikariCP(String name)
	{
		String driver = conf.getString(name + ".driver_class", "com.mysql.jdbc.Driver");
		String jdbcUrl = conf.getString(name + ".url");
		String username = conf.getString(name + ".username");
		String password = conf.getString(name + ".password");

		int maxActive = MyDBConfigManager.DEFAULT_JDBC_MAX_ACTIVE;

		HikariConfig config = new HikariConfig();
		config.setJdbcUrl(jdbcUrl);
		config.setUsername(username);
		config.setPassword(password);
		config.setDriverClassName(driver);
		config.setMaximumPoolSize(maxActive); // 最大连接数
		config.setMinimumIdle(maxActive);
		config.setConnectionTimeout(1000);
		config.setConnectionInitSql("select 1");
		config.setIdleTimeout(MyDBConfigManager.DEFAULT_JDBC_TIME_BETWEEN_EVICTION_RUN); // 空闲超时:60秒

		if(name.startsWith("globaldb.master"))
		{
			config.setReadOnly(false);
		}
		else
		{
			config.setReadOnly(true);
		}

		DataSource ds = new HikariDataSource(config);

		return ds;
	}
}

以上两种连接池都有,我是用 HikariDataSource ! 没办法,谁让它最快呢!

Spring boot 配置多数据源

@Configuration
public class DataSourceConfig {

	@Bean
	@Primary
	@Qualifier(MyDBConfigManager.DB_GLOBAL_MASTER)
	public DataSource gMasterDataSourceBean() {
	    return MyDataSourceFactory.create(MyDBConfigManager.DB_GLOBAL_MASTER);
	}

	@Bean
	@Primary
    public PlatformTransactionManager bfTransactionManager(@Qualifier(MyDBConfigManager.DB_GLOBAL_MASTER)DataSource prodDataSource) {
     return new DataSourceTransactionManager(prodDataSource);
     //return new DynamicDataSourceTransactionManager(prodDataSource);
    }

	@Bean(name = "masterJdbcTemplate")
	@Primary
    public JdbcTemplate gMasterJdbcTemplate(@Qualifier(MyDBConfigManager.DB_GLOBAL_MASTER) DataSource ds)
    {
        return new JdbcTemplate(ds, true);
    }

	@Bean(name = "masterJdbcService")
	@Primary
    public JdbcService masterJdbcService(@Qualifier("masterJdbcTemplate") JdbcTemplate jdbcTemplate)
    {
        return new MyJdbcServiceImpl(jdbcTemplate);
    }

	@Bean
	@Qualifier(MyDBConfigManager.DB_GLOBAL_SLAVE)
	public DataSource gSlaveDataSourceBean() {
	    return MyDataSourceFactory.create(MyDBConfigManager.DB_GLOBAL_SLAVE);
	}

	@Bean(name = "mSlaveJdbcTemplate")
    public JdbcTemplate gSlaveJdbcTemplate(@Qualifier(MyDBConfigManager.DB_GLOBAL_SLAVE) DataSource ds)
    {
        return new JdbcTemplate(ds, true);
    }

	@Bean(name = "slaveJdbcService")
    public JdbcService slaveJdbcService(@Qualifier("mSlaveJdbcTemplate") JdbcTemplate jdbcTemplate)
    {
        return new MyJdbcServiceImpl(jdbcTemplate);
    }

}

HikariCP为什么这么快?

Spring JDBC 教程

数据库连接池性能比对(hikari druid c3p0 dbcp jdbc)