Based on HiKariCP component, the principle of connection pool is analyzed

Pond: Connection;

1, Design and principle

1. Basic case

HiKariCP, as the default connection pool of springboot 2 framework, claims to be the fastest connection pool. Database connection pool, thread pool and object pool mentioned in the previous two articles are based on the idea of pooling in design principle, but have their own characteristics in implementation mode; First, let's look at the basic case of HiKariCP usage:

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

public class ConPool {
    private static HikariConfig buildConfig (){
        HikariConfig hikariConfig = new HikariConfig() ;
        // Basic configuration
        // Connection pool configuration
        return hikariConfig ;
    public static void main(String[] args) throws Exception {
        // Building data sources
        HikariDataSource dataSource = new HikariDataSource(buildConfig()) ;
        // Get connection
        Connection connection = dataSource.getConnection() ;
        // Declare SQL execution
        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery("SELECT count(1) num FROM jt_activity") ;
        // Output execution results
        if ( {
            System.out.println("query-count-result: "+resultSet.getInt("num"));

2. Core related classes

  • HikariDataSource class: collects the relevant information described by the data source, such as configuration, connection pool, connection object, status management, etc;
  • HikariConfig class: maintain the configuration management of data sources and parameter verification, such as userName, passWord, minIdle, maxPoolSize, etc;
  • HikariPool class: provides the core capability of connection pool and object management in the pool, and realizes the query method of pool related monitoring data;
  • ConcurrentBag class: abandoning the blocking queue used in the conventional pool as a container, customize the concurrent container to store connection objects;
  • PoolEntry class: expand the information of connected objects, such as status, time, etc., to facilitate the tracking of these instantiated objects in the container;

Through the analysis of several core classes in the connection pool, we can also intuitively realize the design principle of the source code, which is similar to the object pool application summarized in the previous article, but different components and different developers have their own abstract logic when implementing.

3. Load logic

Construct the data source description through the configuration information, instantiate the connection pool based on the configuration in the construction method, and instantiate the ConcurrentBag container object in the construction of HikariPool; Next, analyze the implementation details from the source code level.

2, Container analysis

1. Container structure

The container ConcurrentBag class provides PoolEntry type connection object storage, basic element management capabilities and object state description; Although it is held by the HikariPool object pool class, the actual operation logic is in this class;

1.1 basic attributes

The most important ones are sharedList shared set, threadList thread level cache and handoffQueue real-time queue;

// Shared object collection to store database connection
private final CopyOnWriteArrayList<T> sharedList;
// Caching thread level connection objects will be used preferentially to avoid being scrambled
private final ThreadLocal<List<Object>> threadList;
// Number of threads waiting to get a connection
private final AtomicInteger waiters;
// Mark whether to close
private volatile boolean closed;
// A queue that processes connections immediately. When there is a waiting thread, the connection is allocated to the waiting thread through the queue
private final SynchronousQueue<T> handoffQueue;

1.2 status description

The IConcurrentBagEntry internal interface in the ConcurrentBag class is implemented by the PoolEntry class, which defines the state of the connection object:

  • STATE_NOT_IN_USE: unused, i.e. idle;
  • STATE_IN_USE: in use;
  • STATE_REMOVED: abandoned;
  • STATE_ In the intermediate state, it is used to expel the object and reserve the connection state;

2. Packaging object

The basic capability of container is to store connected objects, and the management of objects requires a lot of extended tracking information to effectively complete the identification in various scenarios. At this time, it needs the introduction of packaging classes;

// The connection object that the business really uses
Connection connection;
// Last visit time
long lastAccessed;
// Last lending time
long lastBorrowed;
// State description
private volatile int state = 0;
// Whether to expel
private volatile boolean evict;
// Scheduling tasks at the end of the lifecycle
private volatile ScheduledFuture<?> endOfLife;
// Connect the generated Statement object
private final FastList<Statement> openStatements;
// Pool object
private final HikariPool hikariPool;

It should be noted here that FastList class implements the List interface and is customized for HiKariCP components. Compared with ArrayList class, for the pursuit of performance, many range checks are removed in the management of elements.

3, Object management

Based on the general usage of connection pool, let's see how the connection objects are managed, such as being lent, released, abandoned, etc., and the state transition process of the objects under these operations;

1. Initialize

In the above description of loading logic, it has been mentioned that when building the data source, the connection pool will be instantiated according to the configuration. During initialization, the source code will be analyzed based on two core entry points: 1 How many connection objects are instantiated? 2 Connecting objects and converting packaging objects;

In the construction of the connection pool, the checkFailFast method is executed. In this method, the judgment of the minimum idle number of MinIdle is executed. If it is greater than 0, a packaging object is created and put into the container;

public HikariPool(final HikariConfig config) ;
private void checkFailFast() {
    final PoolEntry poolEntry = createPoolEntry();
    if (config.getMinimumIdle() > 0) {

Two problems need to be noticed: the initial state of the created connection packaging object is 0, that is, it is idle; In addition, although the value of MinIdle=4 is set in the case, the judgment here is greater than 0, and only a free object is placed in the container in advance;

2. Borrowing object

When obtaining the connection object from the pool, the actual call is the row method in the container class:

public Connection HikariPool.getConnection(final long hardTimeout) throws SQLException ;
public T ConcurrentBag.borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException ;

The following core steps and logic are involved in the implementation of the arrow method:

public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException
    // Traverse local thread cache
    final List<Object> list = threadList.get();
    for (int i = list.size() - 1; i >= 0; i--) {
       final Object entry = list.remove(i);
       final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry;
       if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) { }
    // Increase the number of waiting threads
    final int waiting = waiters.incrementAndGet();
    try {
        // Traversing Shared collections
        for (T bagEntry : sharedList) {
           if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) { }
        // Polling the handoff queue for a certain period of time
        timeout = timeUnit.toNanos(timeout);
        do {
           final T bagEntry = handoffQueue.poll(timeout, NANOSECONDS);
    } finally {
        // Reduce the number of waiting threads
  • First, reverse traverse the local thread cache. If there is an idle connection, return the object; If not, look for a shared set;
  • Before traversing the Shared shared collection, it will mark the number of waiting threads plus 1. If there is an idle connection, it will be returned directly;
  • When there is no idle connection in the Shared shared collection, the current thread polls the handoffQueue queue for a certain period of time, which may release resources or newly added resources;

Note that when traversing the collection, the extracted objects will judge and update the state. If you get a free object, it will be updated to IN_USE status, and then return;

3. Release object

When releasing the connection object from the pool, the actual call is the request method in the container class:

void HikariPool.recycle(final PoolEntry poolEntry) ;
public void ConcurrentBag.requite(final T bagEntry) ;

When releasing the connection object, first update the object status to idle, and then judge whether there is a waiting thread. In the row method, the waiting thread will enter the polling for a certain time. If not, put the object into the local thread cache:

public void requite(final T bagEntry) {
    // Update status
    // Wait for thread judgment
    for (int i = 0; waiters.get() > 0; i++) {
        if (bagEntry.getState() != STATE_NOT_IN_USE || handoffQueue.offer(bagEntry)) { }
    // Local thread cache
    final List<Object> threadLocalList = threadList.get();
    if (threadLocalList.size() < 50) {
        threadLocalList.add(weakThreadLocals ? new WeakReference<>(bagEntry) : bagEntry);

Note that the state of the connection object is transferred from use to NOT_IN_USE idle; As two core methods in connection pool, borrow and require are responsible for resource creation and recycling;

Finally, this article does not stand on the overall design of HiKariCP components, but only analyzes the tip of the iceberg of connection pool. Although it is only part of the source code, it is enough to highlight the author's ultimate pursuit of performance, such as local thread cache, custom container type, FastList, etc; There must be many supporting reasons for being widely adopted.

4, Reference source code

Application warehouse:

Component encapsulation:

Posted by direwolf on Mon, 18 Apr 2022 22:13:47 +0930