Incident Response

MySQL "Too many connections" (Error 1040): How to Get Back In and Fix It

The app is throwing ERROR 1040, the database refuses everyone including you, and the first instinct is to double max_connections. Here is how to get a session, find what is really eating the slots, and stop it from happening again.

· 13 min read · AG Data Team

MySQL "Too many connections" is one of those errors that looks simple and usually isn't. The application logs fill with ERROR 1040 (HY000): Too many connections, the site starts returning errors, and when you try to log in to see what is going on, MySQL refuses you too. The quick fix everybody reaches for is a bigger max_connections. Sometimes that is right. More often the limit is only where the real problem becomes visible: something upstream is holding connections much longer than it should.

What the error actually means

MySQL has a fixed ceiling on simultaneous client connections, set by max_connections. When every slot is taken, new connections are rejected and the server increments the Connection_errors_max_connections status counter. The default in MySQL 8.4 is 151, with a range from 1 to 100,000. The effective value is also capped by the operating system: it is the lesser of what you set and open_files_limit minus 810. So if you raise max_connections and the file descriptor limit stays low, MySQL may quietly use a smaller number than the one in your config.

On managed services the default is different. Amazon RDS for MySQL derives it from instance memory, roughly one connection per 12 MB, which AWS says comes out at about 60 on a db.t3.micro and around 630 on an 8 GiB class.

Step 1: get yourself a session

You cannot fix a database you cannot reach. MySQL plans for this, as long as it was set up properly:

If neither is available, your options get blunter: stop the application tier that is flooding the server, or restart MySQL. A restart throws away all in-flight work and the evidence of what went wrong, so treat it as the last resort, not the first move.

Step 2: look before you kill

Once you are in, don't start killing threads one by one from SHOW PROCESSLIST. Group them first, so the pattern is visible in a few rows:

SELECT user, SUBSTRING_INDEX(host, ':', 1) AS client,
       command, state, COUNT(*) AS conns, MAX(time) AS max_sec
FROM information_schema.processlist
GROUP BY user, client, command, state
ORDER BY conns DESC;

The result usually points to one of a handful of patterns:

Step 3: free slots without making it worse

Kill selectively, starting with what is idle and belongs to the application. Generating the statements lets you check the list before running it:

SELECT CONCAT('KILL ', id, ';')
FROM information_schema.processlist
WHERE user = 'app_user'
  AND command = 'Sleep'
  AND time > 300;

If sessions are waiting on a lock, kill the blocker, not the queue. Killing a hundred waiting sessions just makes room for a hundred new ones that will wait the same way. Be careful with sessions running long writes: killing them rolls the transaction back, and a big rollback can take as long as the work it undoes.

Then deal with the source. If the application retries instantly and in parallel, it will refill every slot you free within seconds. Scaling the app down or turning a noisy worker off for a few minutes is often the fastest fix there is.

Raising max_connections with SET GLOBAL works without a restart and is a fair emergency measure. But each connection costs memory, and a server running out of RAM is a much worse incident than a server refusing connections. If the root cause is a lock pile-up or a slow query, more slots only means more sessions stuck behind it. If production is down and you are not sure which situation you are in, that is exactly when to call an emergency DBA before making changes you cannot undo.

How big should max_connections be?

There is no magic number. The MySQL manual says the practical maximum depends on the thread library, available RAM, memory used per connection, the workload and your response-time target. It notes that Linux should handle 500 to 1,000 simultaneous connections routinely, and up to 10,000 with many gigabytes of RAM and a light workload per connection.

A more useful way to size it is from the application side. Count every process that holds a pool: application servers, pods, workers, cron jobs, admin tools. Multiply each by its pool size, add a margin for deploys, when old and new instances run at the same time, and compare the result with the limit. Teams on Kubernetes are often surprised here: 40 pods with a pool of 20 is 800 possible connections, and autoscaling can double that in a few minutes.

If the honest total is far above what the server can handle, the answer is fewer connections, not a higher limit: smaller pools, or a proxy that multiplexes many client connections onto fewer server connections (ProxySQL for self-managed MySQL, RDS Proxy on AWS). It is the same idea we describe for PostgreSQL in our connection pooling guide.

Settings worth checking afterwards

Watch it before it fails

"Too many connections" almost never comes out of nowhere. The numbers to watch are all in one place:

SHOW GLOBAL STATUS WHERE Variable_name IN
  ('Threads_connected', 'Max_used_connections',
   'Connection_errors_max_connections', 'Threads_created', 'Connections');
  1. Alert on Threads_connected as a percentage of max_connections, for example at 80%, so you have time to act.
  2. Track Max_used_connections. If it is close to the limit, you have already been close to an outage.
  3. Alert on any increase of Connection_errors_max_connections. Every increase is a client that was refused.
  4. Watch query latency and lock waits along with the connection count. Rising latency is usually what drives connections up.

Key takeaways

MySQL MariaDB Connections Incident Response
Work with us

MySQL refusing connections right now?

We get into locked-up MySQL and MariaDB servers, find what is holding the connections and stabilise production. First response to critical incidents within 15 minutes, 24/7.

Get in touch More from the blog