How to Diagnose and Optimize Production Database Connection Pool Exhaustion
This article walks through the principles of database connection pools, common causes of pool exhaustion such as leaks, mis‑configuration, slow SQL and DB limits, and provides a step‑by‑step methodology with monitoring, thread‑stack analysis, and concrete configuration and SQL optimizations to restore stability.
1. Connection‑Pool Basics
A database connection pool caches JDBC connections. At startup it creates a set of connections; the application borrows a connection, uses it, and returns it to the pool instead of closing it, avoiding the overhead of repeatedly opening and closing sockets.
Key parameters:
minimumIdle – number of idle connections kept alive.
maximumPoolSize – maximum number of connections the pool may create.
connectionTimeout – maximum wait time for a connection before throwing SQLException.
The pool lifecycle is: create idle connections → hand out connections → return them → destroy idle ones after maxLifetime.
2. Common Implementations
HikariCP is the default pool for Spring Boot, praised for its lightweight code (~13 k lines) and performance. Example configuration (application.yml):
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
pool-name: HikariPool-MySQL
minimum-idle: 5
maximum-pool-size: 20
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
connection-test-query: SELECT 1
validation-timeout: 5000HikariCP exposes JMX metrics such as getActiveConnections(), getIdleConnections(), getTotalConnections(), and getThreadsAwaitingConnection().
Druid (Alibaba) adds a web monitoring console and a SQL firewall (WallFilter). Example configuration:
spring:
datasource:
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: false
test-on-return: false
remove-abandoned: true
remove-abandoned-timeout: 300
log-abandoned: true
filter:
stat:
enabled: true
log-slow-sql: true
slow-sql-millis: 2000
wall:
enabled: trueC3P0 is older and slower; it is generally replaced by HikariCP.
3. Impact of Pool Size on DB Performance
Too few connections cause request queuing and time‑outs; too many connections increase DB memory usage and CPU load. A practical starting point is (CPU cores * 2) + number of SSDs. For an 8‑core, 1‑SSD server the estimate is ~17 connections, then refined by load testing.
4. Causes of Pool Exhaustion
4.1 Connection Leaks
When a connection is obtained but not returned, the pool eventually runs out. The most common leak pattern is forgetting to close the connection in a finally block:
Connection conn = null;
try {
conn = dataSource.getConnection();
// use conn
} catch (SQLException e) {
// handle
} finally {
if (conn != null) {
try { conn.close(); } catch (SQLException ignored) {}
}
}Leaks also happen when an exception occurs before the close() call or when transactions are not committed/rolled back.
4.2 Mis‑configured Pool Parameters
Setting maximumPoolSize too low (e.g., 5) for a high‑concurrency service quickly saturates the pool. Overly large maxLifetime can keep connections alive past the DB timeout, causing errors; too short connectionTimeout aborts normal requests.
4.3 Slow SQL
Long‑running queries occupy connections for the duration of the query. Example: a pool of 10 connections processing 100 ms queries can handle 100 req/s; if the same query takes 1 s, throughput drops to 10 req/s, tenfold increase in waiting threads.
4.4 Database‑Side Connection Limits
MySQL max_connections defaults to 151; PostgreSQL to 100. Exceeding these limits yields “too many connections” errors. Each MySQL connection consumes ~256‑400 KB memory.
5. Investigation Methodology & Tools
5.1 Pool Monitoring
HikariCP can be exposed via Spring Boot Actuator or JMX. Example Actuator calls:
curl http://localhost:8080/actuator/metrics/hikaricp.connections.active
curl http://localhost:8080/actuator/metrics/hikaricp.threads.awaitingDruid provides a web UI at /druid/*.
5.2 Thread‑Stack Analysis
Use jstack -l PID > threaddump.txt to locate threads in WAITING for a connection or blocked on pool internals.
5.3 Database Session Inspection
MySQL:
SHOW PROCESSLIST;
SELECT * FROM information_schema.PROCESSLIST WHERE COMMAND!='Sleep' ORDER BY TIME DESC;PostgreSQL:
SELECT pid, usename, state, query FROM pg_stat_activity WHERE state='active';
SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event IS NOT NULL;5.4 Slow‑Query Logging
MySQL slow_query_log=1 with long_query_time=1. Analyze with mysqldumpslow or pt‑query‑digest. PostgreSQL uses log_min_duration_statement and tools like pgBadger or pg_stat_statements.
6. Optimization Recommendations
6.1 Pool Parameter Tuning (HikariCP example for an 8‑core, 8 GB server)
spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 10
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
connection-test-query: SELECT 1
leak-detection-threshold: 60000Calculate max_connections as concurrent_threads × connections_per_thread + reserve, then ensure the DB’s max_connections is at least that value.
6.2 SQL Optimization
Add missing indexes, avoid SELECT *, use proper LIMIT pagination, replace sub‑queries with joins, and batch writes.
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
CREATE INDEX idx_orders_status ON orders(status);
EXPLAIN SELECT * FROM orders WHERE order_id = 123;6.3 Database Parameter Tuning
MySQL example:
[mysqld]
max_connections = 500
innodb_buffer_pool_size = 4G
thread_cache_size = 50PostgreSQL example:
ALTER SYSTEM SET max_connections = 200;
ALTER SYSTEM SET shared_buffers = '2GB';
ALTER SYSTEM SET effective_io_concurrency = 200;6.4 High‑Availability Design
Use primary/secondary data sources with Spring routing, or employ a connection‑pooling proxy such as PgBouncer (transaction mode, default_pool_size=20, max_client_conn=1000).
6.5 Performance Testing
Run JMH benchmarks to verify that connection acquisition meets throughput (>1000 ops/s) and latency (<5 ms) goals.
7. Real‑World Case Study
During a flash‑sale, an e‑commerce service experienced SQLException: timeout errors. Druid showed activeConnections=30 and threadsAwaitingConnection>0. Analysis revealed a missing index on orders.order_id, causing the update statement to scan millions of rows (type=ALL, rows=1,000,000). After adding a composite index (order_id, status) and increasing maximumPoolSize from 30 to 50, active connections peaked at 18, error rate dropped from 1.8 % to 0.2 %, and average response time fell from 450 ms to 120 ms.
8. Monitoring Dashboard
Prometheus scrape configuration for Spring Boot Actuator:
scrape_configs:
- job_name: 'spring-boot-actuator'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['localhost:8080']Grafana queries:
Pool usage %: hikaricp_connections_active / hikaricp_connections * 100 Waiting threads: hikaricp_threads_awaiting_connection Leak count:
hikaricp_leaked_connections9. Common Error Scenarios & Fixes
Pool too small : increase maximumPoolSize and minimumIdle according to concurrency.
Long‑running SQL : add indexes, rewrite queries, set PreparedStatement.setQueryTimeout().
Forgotten close() : use try‑with‑resources.
DB connection limit reached : raise max_connections in DB configuration.
Conclusion
Connection‑pool exhaustion severely impacts application availability. Root causes are typically connection leaks, improper pool settings, slow SQL, or DB‑side limits. Effective diagnosis combines pool metrics, thread‑stack dumps, session inspection, and slow‑query analysis. Remediation spans code‑level fixes (ensure proper close()), pool‑parameter tuning, SQL optimization, and DB configuration. Proactive monitoring and alerting prevent recurrence.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Raymond Ops
Linux ops automation, cloud-native, Kubernetes, SRE, DevOps, Python, Golang and related tech discussions.
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.
