Level: Very Hard — A universal production failure; the root cause is almost always missing or misconfigured pooling.
MySQL has a hard limit, max_connections (default ~151). Each connection also consumes memory (buffers, thread stack). Under load, if the app opens a new connection per request and doesn't reuse them, connections pile up past the limit — and MySQL rejects new ones. It's a connection management problem, not a query-performance problem.
max_connections.
-- How many connections are configured vs currently used?
SHOW VARIABLES LIKE 'max_connections'; -- e.g., 151
SHOW STATUS LIKE 'Threads_connected'; -- currently open
SHOW STATUS LIKE 'Max_used_connections'; -- peak reached
SHOW STATUS LIKE 'Connection_errors_max_connections'; -- rejections
-- See what all those connections are doing
SHOW PROCESSLIST; -- many 'Sleep' rows = idle connections held open = waste
A bigger pool is not better — too many connections cause context-switching and lock contention. A well-known starting formula:
$$ pool\_size = (core\_count \times 2) + effective\_spindle\_count $$
For most apps, a small pool of 10–30 connections per app instance outperforms hundreds. The database does more real work with fewer, busier connections.
{
"maximumPoolSize": 20, // hard cap of connections from this app
"minimumIdle": 5, // keep 5 warm connections ready
"connectionTimeout": 30000, // wait up to 30s for a free connection
"idleTimeout": 600000, // close connections idle > 10 min
"maxLifetime": 1800000 // recycle connections after 30 min
}
max_connections (leave headroom for admin).max_connections as a stopgap — only if memory allows; it's not the real fix.-- Temporary relief; ensure the server has RAM for the extra connections
SET GLOBAL max_connections = 500;
-- Make it permanent in my.cnf: max_connections = 500
-- WARNING: each connection uses memory; too high can OOM the server