NEW

Pulse 2025 Product Roundup: From Monitoring to AI-Native Control Plane

How to Fix MySQL Error 1040: Too Many Connections

ERROR 1040 (HY000): Too many connections is returned by MySQL when a new client attempts to connect and the server has already reached the max_connections limit. The connection is refused immediately — the server does not queue it or wait for a slot to open.

Impact

Every refused connection surfaces as an application error. If the application retries immediately without backoff, it can spike the CPU of both the application servers and MySQL while achieving nothing. In HTTP services the result is a cascade: a brief traffic spike causes connection refusals, which cause request failures, which cause retries, which further exhaust the connection limit. MySQL reserves one administrative connection above max_connections for users with the CONNECTION_ADMIN privilege (root by default), so a DBA can always log in to investigate even when the server is full.

Common Causes

  1. max_connections set too low for the actual number of application threads that open connections
  2. Connection pool misconfigured with a pool size larger than max_connections divided by the number of application instances
  3. Connection leak: application opens connections but never closes or returns them to the pool (missing close(), exception bypassing cleanup, missing finally/context manager)
  4. Long-running queries or transactions holding connections idle for minutes
  5. Spike in traffic without autoscaling on the application tier
  6. Deployments or rolling restarts that briefly double the number of application instances (and their pools)
  7. Monitoring or migration tools opening persistent connections not accounted for in sizing
  8. wait_timeout / interactive_timeout set too high, so idle connections accumulate
  9. Third-party integrations (analytics, BI tools) opening connections outside the pool
  10. Replicas or read-only users connecting to the primary under misdirected load

Troubleshooting and Resolution Steps

  1. Verify the current limit and peak usage:

    SHOW VARIABLES LIKE 'max_connections';
    SHOW GLOBAL STATUS LIKE 'Max_used_connections';
    SHOW GLOBAL STATUS LIKE 'Threads_connected';
    SHOW GLOBAL STATUS LIKE 'Connection_errors_max_connections';
    

    Max_used_connections is the historical peak since the last restart. If it is close to max_connections, you have been near the limit before. Connection_errors_max_connections is the count of times clients were refused.

  2. See every current connection:

    SELECT user, host, db, command, time, state, LEFT(info, 100) AS query
    FROM information_schema.PROCESSLIST
    ORDER BY time DESC;
    

    Group by user and command to find which application is consuming the most slots:

    SELECT user, command, COUNT(*) AS cnt, MAX(time) AS max_time_s
    FROM information_schema.PROCESSLIST
    GROUP BY user, command
    ORDER BY cnt DESC;
    
  3. Identify idle connections that are just sitting open:

    SELECT user, host, db, time AS idle_s
    FROM information_schema.PROCESSLIST
    WHERE command = 'Sleep'
    ORDER BY time DESC
    LIMIT 50;
    

    A large number of Sleep connections with high time values indicates either wait_timeout is too generous or connections are being leaked.

  4. Raise max_connections as immediate relief:

    -- In-session (takes effect immediately, lost on restart)
    SET GLOBAL max_connections = 500;
    
    -- Persist across restarts (MySQL 8.0+)
    SET PERSIST max_connections = 500;
    

    As a rule of thumb: max_connections should not exceed available_RAM_MB / 4 for general workloads, because each connection carries its own per-thread buffer allocations (sort_buffer_size, join_buffer_size, etc.). Do not simply set it to a very large number without also accounting for memory.

  5. Reclaim idle connections faster by lowering wait_timeout:

    -- Current values
    SHOW VARIABLES LIKE 'wait_timeout';
    SHOW VARIABLES LIKE 'interactive_timeout';
    
    -- Lower them (seconds)
    SET GLOBAL wait_timeout = 60;
    SET GLOBAL interactive_timeout = 60;
    SET PERSIST wait_timeout = 60;
    

    The default of 28,800 s (8 hours) keeps idle connections alive far longer than any application needs. 60–300 seconds is typical for OLTP services.

  6. Size the thread cache to avoid thread-creation overhead on reconnects:

    SHOW VARIABLES LIKE 'thread_cache_size';
    SHOW GLOBAL STATUS LIKE 'Threads_created';
    SHOW GLOBAL STATUS LIKE 'Connections';
    
    -- If Threads_created / Connections > 0.01, the cache is too small
    SET GLOBAL thread_cache_size = 50;
    
  7. Audit the connection pool configuration. For HikariCP (Java):

    # Total pool slots across all instances must stay under max_connections
    # leaving room for admins, monitoring, and replication threads
    maximumPoolSize=20
    minimumIdle=5
    idleTimeout=60000        # ms — return idle connections after 60 s
    connectionTimeout=3000   # ms — fail fast if pool is exhausted
    maxLifetime=1800000      # ms — recycle connections every 30 min
    

    For connection pools in general: instances × pool_max_size + overhead ≤ max_connections.

  8. Use ProxySQL to multiplex application connections: ProxySQL sits between your application and MySQL and multiplexes many application-side connections onto a smaller number of backend connections, effectively decoupling the application's concurrency from MySQL's connection limit. This is the standard solution when you have many short-lived microservice instances each with a pool.

  9. Check for connection leaks in the application. Common patterns in Python (SQLAlchemy):

    # Leaky — if something raises, connection is never returned
    conn = engine.connect()
    result = conn.execute(text("SELECT ..."))
    conn.close()
    
    # Safe — context manager guarantees return to pool
    with engine.connect() as conn:
        result = conn.execute(text("SELECT ..."))
    

    In Django: using django.db.connection directly outside a view that has the CONN_MAX_AGE setting set to 0 can leave connections stranded. Set CONN_MAX_AGE to a short positive value or 0 (close after each request) and verify with SHOW STATUS LIKE 'Threads_connected' under load.

  10. Kill idle connections immediately (emergency measure only — prefer fixing the root cause):

    -- Generate KILL statements for all Sleep connections idle > 120 s
    SELECT CONCAT('KILL ', id, ';')
    FROM information_schema.PROCESSLIST
    WHERE command = 'Sleep' AND time > 120;
    

    Copy and run the output. In MySQL 8.0 you can also use KILL CONNECTION id or rely on wait_timeout after you lower it.

  11. Check the Performance Schema for connection history (MySQL 8.0+):

    SELECT user, host, current_connections, total_connections
    FROM performance_schema.accounts
    ORDER BY total_connections DESC;
    

    This shows which user/host pair has opened the most connections over the server's lifetime, useful for identifying misdirected traffic.

Additional Information

  • MySQL always reserves one extra connection above max_connections for users with CONNECTION_ADMIN (8.0+) or SUPER (5.7). This is the only way to log in when the limit is hit.
  • Threads_connected (SHOW STATUS) is the current count; Max_used_connections is the historical peak. Monitor both.
  • Memory consumption per connection is dominated by per-thread sort and join buffers: a rough lower bound is read_buffer_size + read_rnd_buffer_size + sort_buffer_size + join_buffer_size + binlog_cache_size, which can be 4–16 MB per thread by default. Setting max_connections = 10000 on a server with 16 GB RAM will cause OOM kills.
  • Galera Cluster, Group Replication, and read replicas each use their own background connections to the primary; account for these in your connection budget.
  • performance_schema.events_waits_current can show individual thread waits including lock waits and I/O, useful for diagnosing why connections are taking so long that new ones pile up.

Frequently Asked Questions

Q: Can I set max_connections to a very large number to avoid ever hitting the limit? A: No. Each connection allocates per-thread memory, and MySQL does not cap that allocation. Setting max_connections beyond what your RAM can support causes the OS to OOM-kill the process. A connection pool in front of MySQL is the right solution for high concurrency.

Q: My pool size is 10 and I have 20 instances — that's 200 connections. max_connections is 500. Why am I still seeing error 1040? A: Check monitoring tools, cron jobs, replication threads, and any direct database connections from scripts or BI tools that operate outside the pool. SELECT COUNT(*) FROM information_schema.PROCESSLIST gives the real count.

Q: After I raise max_connections, the error goes away but the database slows down. Why? A: More concurrent connections mean more lock contention, context switching, and buffer pool pressure. The right fix is a connection pool with a maximum that matches the database's actual concurrency capacity — usually much lower than the number of application threads.

Q: Does lowering wait_timeout disconnect sessions that are in the middle of a transaction? A: No. wait_timeout applies only to connections in the Sleep state (idle, not executing a query). An active transaction is not affected.

Q: We use serverless functions — each invocation opens a new connection. What do we do? A: Use a connection pooler that can be accessed over TCP, such as PlanetScale's connection pooling, Amazon RDS Proxy, or ProxySQL. Alternatively, use mysql2 (Node) or aiomysql (Python) with a module-level pool that persists across warm invocations.

Q: Is there a way to queue connection attempts instead of immediately refusing them? A: MySQL does not have a built-in queue. ProxySQL has a connection queue (max_connections at the proxy level, with queuing backed by its own pool). RDS Proxy also queues requests up to a configurable timeout.

Subscribe to the Pulse Newsletter

Get early access to new Pulse features, insightful blogs & exclusive events , webinars, and workshops.

We use cookies to provide an optimized user experience and understand our traffic. To learn more, read our use of cookies; otherwise, please choose 'Accept Cookies' to continue using our website.