HikariPool
springboot 默认连接池
https://www.jianshu.com/p/eb85103f29d6
HikariPool数据库连接池配置详解

1. 简介
Hikari连接池目前公认是性能最高的数据库连接池,同时也是SpringBoot2.0以后默认使用的数据库连接池。
2. 关键配置

这些参数在不指定时会有默认值,默认值经过validate方法,会赋不同的值,下面看下具体的源码实现。
dataSource的初始化
@Bean(name = "dataSource")
@Primary
public HikariDataSource dataSource() {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName("className");
hikariConfig.setUsername("user");
hikariConfig.setPassword("password");
hikariConfig.setJdbcUrl("jdbcurl");
hikariConfig.setMaximumPoolSize(100);
hikariConfig.setConnectionTimeout(500);
return new HikariDataSource(hikariConfig);
}
HikariConfig无参构造方法
private static final long CONNECTION_TIMEOUT = SECONDS.toMillis(30);
private static final long VALIDATION_TIMEOUT = SECONDS.toMillis(5);
private static final long IDLE_TIMEOUT = MINUTES.toMillis(10);
private static final long MAX_LIFETIME = MINUTES.toMillis(30);
private static final int DEFAULT_POOL_SIZE = 10;
/**
* Default constructor
*/
public HikariConfig()
{
dataSourceProperties = new Properties();
healthCheckProperties = new Properties();
minIdle = -1;
maxPoolSize = -1;
maxLifetime = MAX_LIFETIME;
connectionTimeout = CONNECTION_TIMEOUT;
validationTimeout = VALIDATION_TIMEOUT;
idleTimeout = IDLE_TIMEOUT;
initializationFailTimeout = 1;
isAutoCommit = true;
String systemProp = System.getProperty("hikaricp.configurationFile");
if (systemProp != null) {
loadProperties(systemProp);
}
}
HikariDataSource初始化方法
/**
* Construct a HikariDataSource with the specified configuration. The
* {@link HikariConfig} is copied and the pool is started by invoking this
* constructor.
*
* The {@link HikariConfig} can be modified without affecting the HikariDataSource
* and used to initialize another HikariDataSource instance.
*
* @param configuration a HikariConfig instance
*/
public HikariDataSource(HikariConfig configuration)
{
// 对参数中不合理的进行修改
configuration.validate();
configuration.copyStateTo(this);
LOGGER.info("{} - Starting...", configuration.getPoolName());
// 构造时就生产pool,不用等到使用才生成
pool = fastPathPool = new HikariPool(this);
LOGGER.info("{} - Start completed.", configuration.getPoolName());
this.seal();
}
validate方法中的validateNumerics
private static final long CONNECTION_TIMEOUT = SECONDS.toMillis(30);
private static final long VALIDATION_TIMEOUT = SECONDS.toMillis(5);
private static final long IDLE_TIMEOUT = MINUTES.toMillis(10);
private static final long MAX_LIFETIME = MINUTES.toMillis(30);
private static final int DEFAULT_POOL_SIZE = 10;
private void validateNumerics()
{
if (maxLifetime != 0 && maxLifetime < SECONDS.toMillis(30)) {
LOGGER.warn("{} - maxLifetime is less than 30000ms, setting to default {}ms.", poolName, MAX_LIFETIME);
maxLifetime = MAX_LIFETIME;
}
if (idleTimeout + SECONDS.toMillis(1) > maxLifetime && maxLifetime > 0) {
LOGGER.warn("{} - idleTimeout is close to or more than maxLifetime, disabling it.", poolName);
idleTimeout = 0;
}
if (idleTimeout != 0 && idleTimeout < SECONDS.toMillis(10)) {
LOGGER.warn("{} - idleTimeout is less than 10000ms, setting to default {}ms.", poolName, IDLE_TIMEOUT);
idleTimeout = IDLE_TIMEOUT;
}
if (leakDetectionThreshold > 0 && !unitTest) {
if (leakDetectionThreshold < SECONDS.toMillis(2) || (leakDetectionThreshold > maxLifetime && maxLifetime > 0)) {
LOGGER.warn("{} - leakDetectionThreshold is less than 2000ms or more than maxLifetime, disabling it.", poolName);
leakDetectionThreshold = 0;
}
}
if (connectionTimeout < 250) {
LOGGER.warn("{} - connectionTimeout is less than 250ms, setting to {}ms.", poolName, CONNECTION_TIMEOUT);
connectionTimeout = CONNECTION_TIMEOUT;
}
if (validationTimeout < 250) {
LOGGER.warn("{} - validationTimeout is less than 250ms, setting to {}ms.", poolName, VALIDATION_TIMEOUT);
validationTimeout = VALIDATION_TIMEOUT;
}
if (maxPoolSize < 1) {
maxPoolSize = (minIdle <= 0) ? DEFAULT_POOL_SIZE : minIdle;
}
if (minIdle < 0 || minIdle > maxPoolSize) {
minIdle = maxPoolSize;
}
}
3 Hikari源码解析
3.1 获取连接
1、Hikari中的核心类为HikariDataSource,表示Hikari连接池中的数据源,实现了DataSource接口的getConnection方法,getConnection方法源码如下:
/** 连接池对象
* fastPathPool 会在初始化时创建
* pool 是在获取连接数创建
* volatile修饰pool导致每次读pool都要从主存加载,每次写也要写回主存,性能不如没volatile修饰的fastPathPool
* */
private final HikariPool fastPathPool;
private volatile HikariPool pool;
/** 获取连接*/
public Connection getConnection() throws SQLException
{
if (isClosed()) {
throw new SQLException("HikariDataSource " + this + " has been closed.");
}
/** 如果fastPathPool存在则直接获取连接,有参构造方法都有*/
if (fastPathPool != null) {
return fastPathPool.getConnection();
}
/** 如果没有fastPathPool 则创建HikariPool对象 */
// 双检实现单例,pool用volatile修饰,防止指令重排序
HikariPool result = pool;
if (result == null) {
synchronized (this) {
result = pool;
if (result == null) {
validate();
LOGGER.info("{} - Starting...", getPoolName());
try {
/** 初始化创建HikariPool对象*/
pool = result = new HikariPool(this);
this.seal();
}
catch (PoolInitializationException pie) {
//
}
}
}
}
/** 调用pool的getConnection()方法获取连接*/
return result.getConnection();
}
getConnection方法逻辑不多,主要是调用了HikariPool的getConnection()方法,而HikariDataSource中有两个HikariPool对象,一个是fastPathPool是在HikariPool有参构造函数中创建, 如果没有创建fastPathPool,那么就会在getConnection方法时创建pool对象。
很显然pool对象是由volatile关键字修饰的,而fastPathPool是final类型的,所以fastPathPool的效率会比pool要高,所以推荐使用HikariDataSource有参构造函数进行初始化。
2、由上可知获取连接的逻辑是在HikariPool的getConnection方法中,继续分析HikariPool的getConnection方法,源码如下:
/** 获取连接*/
public Connection getConnection(final long hardTimeout) throws SQLException
{
/** 获取锁*/
suspendResumeLock.acquire();
final long startTime = currentTime();
try {
long timeout = hardTimeout;
do {
/** 从ConcurrentBag中借出一个PoolEntry对象 */
PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS);
if (poolEntry == null) {
break; // We timed out... break and throw exception
}
final long now = currentTime();
/** 判断连接是否被标记为抛弃 或者 空闲时间过长, 是的话就关闭连接*/
if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > aliveBypassWindowMs && !isConnectionAlive(poolEntry.connection))) {
closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE);
timeout = hardTimeout - elapsedMillis(startTime);
}
else {
metricsTracker.recordBorrowStats(poolEntry, startTime);
/** 通过Javassist创建代理连接*/
return poolEntry.createProxyConnection(leakTaskFactory.schedule(poolEntry), now);
}
} while (timeout > 0L);
metricsTracker.recordBorrowTimeoutStats(startTime);
throw createTimeoutException(startTime);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new SQLException(poolName + " - Interrupted during connection acquisition", e);
}
finally {
/** 释放锁*/
suspendResumeLock.release();
}
}
核心步骤只有两步,一个是调用ConcurrentBag的borrow方法借用一个PoolEntry对象,第二步调用调用PoolEntry的createProxyConnection方法动态生成代理connection对象。
这里涉及到了两个核心的类,分别是ConcurrentBag和PoolEntry。
PoolEntry
PoolEntry顾名思义是连接池的节点,实际也可以看作是一个Connection对象的封装,连接池中存储的连接就是以PoolEntry的方式进行存储。
PoolEntry内部属性如下:
属性 | 类型 | 描述 |
---|---|---|
connection | Connection | 数据库连接 |
lastAccessed | long | 上一次访问时间 |
lastBorrowed | long | 上一次借出时间 |
state | volatile int | 当前状态 |
evict | volatile boolean | 是否该丢弃 |
openStatements | FastList | 打开的statement集合 |
hikariPool | HikariPool | 关联的HikariPool对象 |
isReadOnly | boolean | 是否只读 |
isAutoCommit | boolean | 是否自动提交 |
ConcurrentBag
ConcurrentBag直意就是并发包,本质就是连接池的主体,存储连接的封装对象PoolEntry,另外做了并发控制来解决连接池的并发问题。
ConcurrentBag的内部属性如下:

ConcurrentBag借出一个元素
/** 借出一个对象 */
public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException
{
/** 1.从ThreadLocal中获取当前线程绑定的对象集合 */
final List<Object> list = threadList.get();
/** 1.1.如果当前线程变量中存在就直接从list中返回一个*/
for (int i = list.size() - 1; i >= 0; i--) {
final Object entry = list.remove(i);
sharedList
从ThreadLocal中获取连接失败之后,会再次尝试从sharedList中获取,sharedList集合存在初始化的PoolEntry。在ConcurrentBag初始化的,会初始化指定数量的PoolEntry对象存入sharedList,源码如下:
/** ConcurrentHand
* IBagStateListener bag状态监听器,HikariPool实现了IBagStateListener接口
* 所以构造器传入的listener实际就是HikariPool对象
* */
public ConcurrentBag(final IBagStateListener listener)
{
this.listener = listener;
//是否使用弱引用
this.weakThreadLocals = useWeakThreadLocals();
//初始化阻塞队列
this.handoffQueue = new SynchronousQueue<>(true);
//初始化等待连接数
this.waiters = new AtomicInteger();
//初始化sharedList
this.sharedList = new CopyOnWriteArrayList<>();
if (weakThreadLocals) {
this.threadList = ThreadLocal.withInitial(() -> new ArrayList<>(16));
}
else {
this.threadList = ThreadLocal.withInitial(() -> new FastList<>(IConcurrentBagEntry.class, 16));
}
}
HikariPool内部属性包含了ConcurrentBag对象,在HikariPool初始化时会创建ConcurrentBag对象,所以ConcurrentBag的构造函数是在HikariPool初始化时调用,HikariPool构造函数如下:
public HikariPool(final HikariConfig config)
{
super(config);
//初始化ConcurrentBag对象
this.connectionBag = new ConcurrentBag<>(this);
//创建SuspendResumeLock对象
this.suspendResumeLock = config.isAllowPoolSuspension() ? new SuspendResumeLock() : SuspendResumeLock.FAUX_LOCK;
/** 初始化线程池,houseKeeping可以理解为保持空间充足的意思,空间也就是连接池,该线程池的作用就是保持连接池中合适的连接数的作用 */
this.houseKeepingExecutorService = initializeHouseKeepingExecutorService();
/** 设置属性*/
checkFailFast();
if (config.getMetricsTrackerFactory() != null) {
setMetricsTrackerFactory(config.getMetricsTrackerFactory());
}
else {
setMetricRegistry(config.getMetricRegistry());
}
setHealthCheckRegistry(config.getHealthCheckRegistry());
handleMBeans(this, true);
ThreadFactory threadFactory = config.getThreadFactory();
/** 根据配置的最大连接数,创建链表类型阻塞队列 */
LinkedBlockingQueue<Runnable> addQueue = new LinkedBlockingQueue<>(config.getMaximumPoolSize());
this.addConnectionQueue = unmodifiableCollection(addQueue);
/** 初始化创建连接线程池*/
this.addConnectionExecutor = createThreadPoolExecutor(addQueue, poolName + " connection adder", threadFactory, new ThreadPoolExecutor.DiscardPolicy());
/** 初始化关闭连接线程池*/
this.closeConnectionExecutor = createThreadPoolExecutor(config.getMaximumPoolSize(), poolName + " connection closer", threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());
this.leakTaskFactory = new ProxyLeakTaskFactory(config.getLeakDetectionThreshold(), houseKeepingExecutorService);
/** 创建保持连接池连接数量的任务*/
this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(new HouseKeeper(), 100L, housekeepingPeriodMs, MILLISECONDS);
if (Boolean.getBoolean("com.zaxxer.hikari.blockUntilFilled") && config.getInitializationFailTimeout() > 1) {
addConnectionExecutor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
addConnectionExecutor.setMaximumPoolSize(Runtime.getRuntime().availableProcessors());
final long startTime = currentTime();