Understanding Druid Connection Pool: Initialization, Connection Acquisition, Execution, and Recycling
This article provides a comprehensive technical walkthrough of the Druid database connection pool, covering its architecture, initialization flow, connection acquisition and release mechanisms, execution and exception handling, as well as recommended configurations and monitoring practices for optimal performance.
1 Introduction
When troubleshooting database connection‑pool issues such as connection starvation, exceptions, long‑running holds, or performance overhead, many developers only have a superficial understanding of connection pools. This article explains the working principle of the Druid connection pool and offers practical recommendations.
2 Druid Overview
With a connection pool, multiple database connections are created at application startup and stored in the pool. When a client request arrives, a connection is taken from the pool; after the request finishes, the connection is returned to the pool, similar to a shared‑bike system.
Resource reuse : Reusing connections avoids the cost of frequent creation and release.
Performance improvement : Connections are ready for immediate use, reducing response latency.
Optimized resource allocation : Limits on maximum active connections prevent a single application from monopolizing the database.
Connection management : Idle‑timeouts and forced reclamation prevent resource leaks.
3 Initialization Process init()
The initialization is triggered on the first getConnection() call or when init() is invoked directly.
3.1 LogStatsThread (Druid-ConnectionPool-Log-)
If timeBetweenLogStatsMillis > 0, the thread logs pool statistics at the configured interval.
3.2 CreateConnectionThread (Druid-ConnectionPool-Create-)
This background thread creates new physical connections when the empty condition signal is received and the pool has not reached maxActive .
if (activeCount + poolingCount >= maxActive) {
empty.await();
continue;
}3.2.1 Condition to create a new connection
The thread checks whether creating a new connection would exceed maxActive . If so, it waits for the next empty signal.
3.2.2 createPhysicalConnection() – creating a physical JDBC connection
public PhysicalConnectionInfo createPhysicalConnection() throws SQLException {
// ... (omitted for brevity)
Connection conn = createPhysicalConnection(url, physicalConnectProperties);
// initialization and validation logic
return new PhysicalConnectionInfo(conn, ...);
}3.2.3 put(holder, createTaskId) – placing a connection into the pool
private boolean put(DruidConnectionHolder holder, long createTaskId) {
lock.lock();
try {
if (poolingCount >= maxActive) return false;
connections[poolingCount] = holder;
incrementPoolingCount();
notEmpty.signal();
notEmptySignalCount++;
return true;
} finally {
lock.unlock();
}
}3.3 DestroyConnectionThread (Druid-ConnectionPool-Destroy-)
This thread periodically scans idle connections (interval defined by timeBetweenEvictionRunsMillis ) and performs eviction, keep‑alive probing, or forced reclamation of abandoned connections.
public void run() {
shrink(true, keepAlive);
if (isRemoveAbandoned()) {
removeAbandoned();
}
}3.3.1 shrink(checkTime, keepAlive) – eviction logic
public void shrink(boolean checkTime, boolean keepAlive) {
// iterate over connections, evaluate idle time, physical timeout, etc.
// evict or keepAlive as appropriate
}3.3.2 removeAbandoned() – leak detection
If removeAbandoned is enabled and a connection has been borrowed longer than removeAbandonedTimeoutMillis , the connection is forcibly closed and returned to the pool.
public int removeAbandoned() {
// iterate over active connections, detect leaks, close them
return removeCount;
}4 Get Connection Process getConnection()
The core method getConnectionDirect() obtains an available connection from the pool.
public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
for (;;) {
DruidPooledConnection pc = getConnectionInternal(maxWaitMillis);
if (testOnBorrow) {
boolean valid = testConnectionInternal(pc.holder, pc.conn);
if (!valid) {
discardConnection(pc.holder);
continue;
}
}
// optional testWhileIdle logic omitted for brevity
if (removeAbandoned) {
// record stack trace and borrow time
activeConnections.put(pc, PRESENT);
}
return pc;
}
}4.1 getConnectionInternal() – fetch a raw connection
private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException {
// wait for a free connection or create a new one
// increment usage count
return new DruidPooledConnection(holder);
}4.2 takeLast() – block until a connection is available
DruidConnectionHolder takeLast() throws InterruptedException, SQLException {
while (poolingCount == 0) {
emptySignal();
notEmptyWaitThreadCount++;
try {
notEmpty.await();
} finally {
notEmptyWaitThreadCount--;
}
}
decrementPoolingCount();
DruidConnectionHolder last = connections[poolingCount];
connections[poolingCount] = null;
return last;
}5 Execution & Exception Handling
MyBatis invokes SqlSessionTemplate$SqlSessionInterceptor.invoke() , which ultimately calls DruidPooledPreparedStatement.execute() . If an exception occurs, DruidDataSource.handleConnectionException() determines whether the connection is fatal and may discard it.
public boolean execute() throws SQLException {
conn.beforeExecute();
try {
return stmt.execute();
} catch (Throwable t) {
errorCheck(t);
throw checkException(t);
} finally {
conn.afterExecute();
}
}5.1.1 handleConnectionException()
public void handleConnectionException(DruidPooledConnection pc, Throwable t, String sql) throws SQLException {
if (t instanceof SQLException) {
SQLException sqlEx = (SQLException) t;
if (exceptionSorter != null && exceptionSorter.isExceptionFatal(sqlEx)) {
handleFatalError(pc, sqlEx, sql);
}
throw sqlEx;
} else {
throw new SQLException("Error", t);
}
}6 Recycle Process recycle()
After SQL execution, the application calls DruidPooledConnection.recycle() , which delegates to DruidDataSource.recycle() to reset and return the connection to the pool.
public void recycle() throws SQLException {
DruidConnectionHolder holder = this.holder;
DruidAbstractDataSource ds = holder.getDataSource();
ds.recycle(this);
}6.1 DruidDataSource.recycle()
protected void recycle(DruidPooledConnection pc) throws SQLException {
DruidConnectionHolder holder = pc.holder;
Connection physical = holder.conn;
try {
holder.reset();
if (phyMaxUseCount > 0 && holder.useCount >= phyMaxUseCount) {
discardConnection(holder);
return;
}
if (testOnReturn) {
// optional validation logic
}
if (phyTimeoutMillis > 0 && System.currentTimeMillis() - holder.connectTimeMillis > phyTimeoutMillis) {
discardConnection(holder);
return;
}
lock.lock();
try {
boolean result = putLast(holder, System.currentTimeMillis());
recycleCount++;
if (!result) JdbcUtils.close(holder.conn);
} finally {
lock.unlock();
}
} catch (Throwable e) {
// error handling omitted
}
}6.2 putLast() – return connection to the tail of the pool and signal notEmpty
boolean putLast(DruidConnectionHolder e, long lastActiveTimeMillis) {
if (poolingCount >= maxActive || e.discard) return false;
e.lastActiveTimeMillis = lastActiveTimeMillis;
connections[poolingCount] = e;
incrementPoolingCount();
notEmpty.signal();
notEmptySignalCount++;
return true;
}7 Summary
init() : creates initialSize connections and starts LogStatsThread, CreateConnectionThread, and DestroyConnectionThread.
getConnection() : removes a connection from the pool; the connection is bound to the requesting thread.
recycle() : returns the connection to the pool, making it available for reuse.
7.2 Condition‑Signal Collaboration
When a request finds the pool empty, it sends an empty signal and waits on notEmpty . The CreateConnectionThread reacts to empty by creating a new connection (if allowed) and then signals notEmpty .
When a connection is recycled, notEmpty is signaled, waking any waiting request threads.
7.3 Detection and Destruction Logic
Borrowing: optional testOnBorrow and testWhileIdle validation.
Execution: fatal exceptions identified by exceptionSorter trigger handleFatalError() .
Returning: testOnReturn , usage‑count limit phyMaxUseCount , and physical timeout phyTimeoutMillis may cause discarding.
DestroyConnectionThread: evicts idle connections based on minEvictableIdleTimeMillis , maxEvictableIdleTimeMillis , keep‑alive probing, and abandoned‑connection removal.
8 Common & Recommended Configuration
Typical Spring XML configuration for a Druid datasource:
<bean id="userdataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="name" value="userdataSource"/>
<property name="url" value="${userdataSource_url}"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="initialSize" value="1"/>
<property name="minIdle" value="3"/>
<property name="maxActive" value="20"/>
<property name="maxWait" value="60000"/>
<property name="validationQuery" value="SELECT 1"/>
<property name="testOnBorrow" value="false"/>
<property name="testWhileIdle" value="true"/>
<property name="testOnReturn" value="false"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="minEvictableIdleTimeMillis" value="300000"/>
</bean>This configuration creates one connection at startup, keeps at least three idle connections, caps the total at twenty, waits up to 60 seconds for a connection, and performs periodic validation and eviction.
9 Monitoring
By leveraging Druid’s SPI extension points, a custom monitoring component can export metrics to Prometheus, providing real‑time visibility of pool size, active/idle counts, wait times, and error rates.
Reference: [1] Druid – https://github.com/alibaba/druid [2] "聊聊数据库连接池 Druid" – https://www.cnblogs.com/makemylife/p/17889584.html
Zhuanzhuan Tech
A platform for Zhuanzhuan R&D and industry peers to learn and exchange technology, regularly sharing frontline experience and cutting‑edge topics. We welcome practical discussions and sharing; contact waterystone with any questions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.