HikariCP Analysis (1)
SpringBoot 2.x officially announced the use of HikariCP as the default database connection pool, as a new generation of connection pool, HikariCP performance is very good, and the code is very simple, concurrent design is also worth learning from us
Initialization process
Core components
Today we will first analyze the HikariCP initialization process, starting with the previous image
The diagram above shows the general logic of obtaining connections, involving the following classes, which contain most of the HikariCP logic
HikariDataSource
As the default data source implementation class loaded when Spring Boot2.x is started, it is provided for direct use by the upper layer service and holdsHikariPool
objectHikariPool
Real connection pool management provides the ability to obtain, discard, close, and reclaim connections for upper-layer use and internal storageConcurrentBag
objectConcurrentBag
The real existence of the connection is where the inside holds oneCopyWriteArrayList
objectsharedList
And also throughthreadList
Provides thread-level connection cachingProxyFactory
Generate wrapper classesHikariProxyConnection
Since it is a wrapper class, there must be some additional operation package in it. Javassist technology is also involved here. See the logic for detailsJavassistProxyFactory
Start the process
First, service startup is the logic for loading the connection pool
@Bean(name = "agreementDataSource")
@ConfigurationProperties(prefix = "mybatis")
public DataSource agreementDataSource(a) {
return DataSourceBuilder.create().build();
}
@Bean(name = "readSource")
@ConfigurationProperties(prefix = "mybatis.read")
public DataSource readSource(a) {
return DataSourceBuilder.create().build();
}
Copy the code
The Datasource bean is simply initialized. The HikariPool inside the Datasource is not initialized. The logic to initialize the HikariPool is set to the first connection
Obtaining connections for the first time
@Override
public Connection getConnection(a) throws SQLException
{
if (isClosed()) {
throw new SQLException("HikariDataSource " + this + " has been closed.");
}
if(fastPathPool ! =null) {
return fastPathPool.getConnection();
}
// See http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
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) {
if (pie.getCause() instanceof SQLException) {
throw (SQLException) pie.getCause();
}
else {
throw pie;
}
}
LOGGER.info("{} - Start completed.", getPoolName()); }}}return result.getConnection();
}
Copy the code
On the first request, the connection pool is initialized to determine whether the current connection pool is empty. If it is empty, the connection pool is initialized with two HikariPool objects. The performance is not as good as fastPathPool without volatile modifiers. We use the no-parameter construct for initialization, so pool is used here
fastPathPool
:final
Embellish, determined at construction time. If null is used with a no-parameter construct, use the parameterized construct andpool
The samepool
:volatile
Modifier, the no-argument construct is not setpool
In thegetConnection
When constructingpool
, with parameters to construct andfastPathPool
The same
Initialize the connection pool
The initialization code is as follows:
public HikariPool(final HikariConfig config){
super(config);
this.connectionBag = new ConcurrentBag<>(this);
this.suspendResumeLock = config.isAllowPoolSuspension() ? new SuspendResumeLock() : SuspendResumeLock.FAUX_LOCK;
this.houseKeepingExecutorService = initializeHouseKeepingExecutorService();
checkFailFast();
if(config.getMetricsTrackerFactory() ! =null) {
setMetricsTrackerFactory(config.getMetricsTrackerFactory());
}
else {
setMetricRegistry(config.getMetricRegistry());
}
setHealthCheckRegistry(config.getHealthCheckRegistry());
registerMBeans(this);
ThreadFactory threadFactory = config.getThreadFactory();
LinkedBlockingQueue<Runnable> addConnectionQueue = new LinkedBlockingQueue<>(config.getMaximumPoolSize());
this.addConnectionQueue = unmodifiableCollection(addConnectionQueue);
this.addConnectionExecutor = createThreadPoolExecutor(addConnectionQueue, 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, HOUSEKEEPING_PERIOD_MS, MILLISECONDS);
}
Copy the code
This method is used to initialize the entire connection pool and all the properties in the pool
- using
config
Initialize the various connection pool properties and generate a data source for producing physical connectionsDriverDataSource
- Initialize the core class that holds connection objects
connectionBag
- Initialize an object of type thread pool for delayed tasks
houseKeepingExecutorService
, used for subsequent execution of some delay/timing tasks (such as connection leak check delay task, in additionmaxLifeTime
This object is also responsible for retrieving and closing the connection. - Preheat the connection pool,
HikariCP
Will be in the flowcheckFailFast
Initialization of a connection object into the pool, of course, trigger the process is guaranteedinitializationTimeout > 0
This configuration property represents the time left for the warmup operation (the default value of 1 does not occur when the warmup fails). withDruid
throughinitialSize
Control the number of preheating connected objects is different,HikariCP
Only one connection object is preheated into the pool. - Initialize a thread pool object
addConnectionExecutor
Is used to extend connection objects - Initialize a thread pool object
closeConnectionExecutor
To close the connection object
Pool.getconnection (), HikariCP gets the connection, and the internal management of the connection is analyzed later
Monitoring the related
Finally, the content of monitoring in HikariCP should be added. In the process of our use, we should know the status and pressure of the whole database connection pool in real time, and inform the developers in time when there is a problem
HikariCP built-in implementation of a variety of monitoring implementation, if you need to customize the implementation, you can also expand the corresponding interface, and set in the initial monitoring configuration
public interface MetricsTrackerFactory {
/**
* Create an instance of an IMetricsTracker.
*
* @param poolName the name of the pool
* @param poolStats a PoolStats instance to use
* @return a IMetricsTracker implementation instance
*/
IMetricsTracker create(String poolName, PoolStats poolStats);
}
Copy the code
Here’s a look at some of the metrics implemented by default
public static final String HIKARI_METRIC_NAME_PREFIX = "hikaricp";
private static final String METRIC_CATEGORY = "pool";
private static final String METRIC_NAME_WAIT = HIKARI_METRIC_NAME_PREFIX + ".connections.acquire";
private static final String METRIC_NAME_USAGE = HIKARI_METRIC_NAME_PREFIX + ".connections.usage";
private static final String METRIC_NAME_CONNECT = HIKARI_METRIC_NAME_PREFIX + ".connections.creation";
private static final String METRIC_NAME_TIMEOUT_RATE = HIKARI_METRIC_NAME_PREFIX + ".connections.timeout";
private static final String METRIC_NAME_TOTAL_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections";
private static final String METRIC_NAME_IDLE_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections.idle";
private static final String METRIC_NAME_ACTIVE_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections.active";
private static final String METRIC_NAME_PENDING_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections.pending";
private static final String METRIC_NAME_MAX_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections.max";
private static final String METRIC_NAME_MIN_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections.min";
Copy the code
hikaricp_connections_pending
Monitoring is very necessary- Number of threads waiting for connections. When troubleshooting performance problems, this indicator is an important reference indicator. If the number of threads waiting for connections is large for a certain period of time, you can consider increasing the size of the database connection pool
hikaricp_connections_acquire
Monitoring is very necessary- Connect to get the time. You can view the time it takes to obtain a connection within a time range
hikaricp_connections_timeout
Monitoring is very necessary- Gets the number of threads that timed out the connection. An increase in the number of timeouts is either a problem with the database (a database operation takes too long to return a connection) or there are not enough connections in the connection pool
hikaricp_connections_active
Need to monitor- Number of current active connections. You can adjust the configuration based on the increasing trend of the number of active connections
Configuration interpretation
HikariDataSource validates the pool attributes while initializing them. If the pool attributes are invalid, they are reset to the default value after validate
Important configuration
connectionTimeout
:- Description: Maximum number of milliseconds to wait for connections from the pool
- Default value: 30000ms
validate
Reset: Reset to 30 seconds if less than 250 ms- Recommended configuration: The default value is used when the pool connection is not tight. In scenarios with frequent interactions, you can adjust the pool size to facilitate monitoring and adjustment
idleTimeout
:- Description: Maximum time a connection is allowed to remain idle in the pool
- Default value: 600000ms
validate
Reset: IfidleTimeout
+ 1 seconds >maxLifetime
且maxLifetime
> 0 is reset to 0 (never exits); ifidleTimeout
! If = 0 and less than 10 seconds, it is reset to 10 seconds- Recommended configuration: This value is available only when
minimumIdle
Defined as less thanmaximumPoolSize
, and the number of connections in the current pool is greater thanminimumIdle
In this case, use the default Settings
maxLifetime
:- Description: Maximum connection lifetime in the pool
- Default value: 1800000ms
validate
Reset: If it is not 0 and less than 30 seconds, it will be reset to 30 minutes- Recommended configuration: It is best to set it to more than database timeout
wait_timeout
Small enough to avoid database level timeouts, but connections in the pool are still alive
minimumIdle
:- Description: Minimum number of free connections maintained in the pool
- Default value: 10
validate
Reset:minIdle
< 0 orminIdle
>maxPoolSize
Is reset tomaxPoolSize
- Recommended configuration: see online information are recommended Settings and
maximumPoolSize
This maximally reduces the likelihood of blocking access connections due to capacity expansion and reduction, but has been found to be less necessary in practice. This can be combined with the performance of online connection pool monitoring. It is best to keep this configuration greater than the activie_Connection value that can guarantee the peak traffic. For example, the number of active connections in our service at peak times is about 10, so 10 ~ 20 can be set
maximumPoolSize
:- Description: Maximum number of connections in the pool, including idle and active connections
- Default value: 10
validate
Reset: IfmaxPoolSize
If the value is less than 1, it will be reset. whenminIdle
<= 0 is reset toDEFAULT_POOL_SIZE
It is 10; ifminIdle
> 0, reset tominIdle
The value of the- Recommended configuration: To ensure that the bottom connection pool can withstand extreme traffic, you can set this value to several times that of
minimumIdle
In the case of single-database multi-instance deployment, the number of connections to all instances is less than the upper limit of the database
conclusion
HikariCP
Initialization is done by default when the first connection is acquiredHikariCP
Some important monitoring indicators, so as to know the connection pool online usageHikariCP
Some important configurations, default configurations combined with the actual situation of their own application services, slowly tune to ensure that the connection pool does not become a bottleneck in the whole service