You're getting 'too many connections' or 'connection pool exhausted'. Your app can't talk to the database. What's actually happening here is that MySQL has a hard limit on how many connections it can handle at once. When all those slots are full, new connections get rejected.
I've seen this error in PHP apps with PDO, Node.js with mysql2, and Python with SQLAlchemy. The fix depends on which of the three common causes you're hitting. Let's start with the one that fixes 80% of cases.
Cause #1 – Connections not being closed after use
This is the most common. Your code opens a connection but never closes it explicitly. In PHP with PDO, if you don't set the connection object to null, the connection stays open until the script ends. But if your script is long-running (like a worker queue), that connection never dies.
Here's the real fix: for short scripts, wrap your PDO connection in a try-finally block and close it in the finally block. For long-running processes, use a connection pool library that manages the lifecycle for you.
Example fix for PHP PDO:
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
try {
// your queries
} finally {
$dbh = null; // this actually closes the connection
}
The reason this works: setting the PDO object to null triggers PHP's destructor, which sends a QUIT command to MySQL. Without it, the connection sits idle until MySQL's wait_timeout kicks in (default 8 hours).
For Node.js with mysql2, never leave a connection hanging. Always call connection.release() or pool.end() after your query.
Cause #2 – max_connections set too low for your workload
Your code is clean, connections close properly, but you still hit the limit. Check your MySQL config:
SHOW VARIABLES LIKE 'max_connections';
Default is 151 for MySQL 8.0. If your app has 100 web servers each holding 5 connections in a pool, that's 500 connections right there. You'll exhaust 151 fast.
The fix: increase max_connections in your my.cnf file. But don't go wild. Each connection uses RAM (about 256KB to 1MB depending on buffers). I've seen people set it to 1000 on a 2GB server and crash from memory exhaustion.
For production, use this formula:
max_connections = (available_ram * 0.8) / per_connection_ram
Where per_connection_ram you can estimate from SHOW GLOBAL STATUS LIKE 'Max_used_connections' and SHOW GLOBAL STATUS LIKE 'Threads_connected'.
Also consider using a connection pool in your app with a fixed size. For example, in a Java app with HikariCP, set maximumPoolSize to something like 20. That way your app doesn't open infinite connections.
Cause #3 – Abandoned connections from killed or timed-out queries
This one's trickier. Your code closes connections, your pool size is reasonable, but connections still pile up. Look at SHOW PROCESSLIST. See a bunch of connections in 'Sleep' state or 'Query' state from hours ago?
What's actually happening here is a query took too long (longer than wait_timeout or interactive_timeout), MySQL killed it, but the application didn't detect the death. The app thinks it has an open connection, but it's dead on the server side. Eventually all pool slots get filled with these zombie connections.
The fix: set wait_timeout and interactive_timeout to something reasonable for your app. 30 seconds to 5 minutes is typical for web apps. Also enable skip_name_resolve to avoid DNS delays that can cause timeouts.
Run this in MySQL:
SET GLOBAL wait_timeout = 300;
SET GLOBAL interactive_timeout = 300;
And add to my.cnf:
[mysqld]
wait_timeout = 300
interactive_timeout = 300
skip_name_resolve
For apps using PDO, also set PDO::ATTR_TIMEOUT to 2 seconds so your app doesn't hang waiting for a dead connection.
Quick reference table
| Cause | Symptom | Fix |
|---|---|---|
| Unclosed connections | Connections stay in 'Sleep' state | Close connections explicitly in code; use try-finally |
| Low max_connections | Error says 'too many connections' | Increase max_connections; add app-level connection pool |
| Abandoned dead connections | Zombie connections in process list | Reduce wait_timeout; enable skip_name_resolve; detect dead connections |
Start with cause #1. That's where I've seen 9 out of 10 cases of connection pool exhaustion. If that doesn't fix it, check your max_connections and timeout settings. Don't just increase limits blindly — find the root cause first.