WordPress performance: Implementing MySQL Connection Pooling with ProxySQL
WordPress performance hinges on database efficiency. Learn how to implement MySQL connection pooling using ProxySQL to prevent exhaustion under high load.
Last month, I spent about three days debugging a sudden "Too many connections" error on a high-traffic e-commerce plugin I maintain. The site was hitting roughly 400 concurrent users, and the standard PHP-FPM to MySQL handshake was creating a bottleneck that brought the entire checkout flow to a crawl.
We usually treat WordPress database connections as ephemeral: a request comes in, a connection opens, we run a few queries, and we close it. Under low load, this is fine. Under high concurrency, the overhead of establishing those TCP handshakes—and the resulting memory pressure on the MySQL server—is a recipe for disaster.
Why standard WordPress connections fail at scale
When you're running a busy site, every single PHP worker tries to maintain its own connection pool to the database. If you have 100 PHP-FPM workers, that’s potentially 100 active connections to MySQL, even if half of those workers are currently idle or waiting on disk I/O.
I initially tried increasing max_connections in my.cnf. That just shifted the problem. The MySQL server started thrashing, spending more time managing context switches than executing queries. That’s when I realized that MySQL connection pooling isn't just an optimization; it’s a prerequisite for stability.
Architecting a ProxySQL layer
To solve this, I introduced ProxySQL between the application and the database. It acts as a smart proxy that maintains a persistent pool of connections to the MySQL backend, allowing the PHP processes to connect to the proxy instead of the database directly.
The architecture looks like this:
- PHP-FPM connects to ProxySQL on
127.0.0.1:6033(the proxy port). - ProxySQL maintains a "warm" pool of connections to the actual MySQL server.
- When a WordPress request finishes, the connection to the DB isn't dropped; it’s returned to the ProxySQL pool for the next request.
This setup significantly improves WordPress performance by eliminating the TCP handshake latency. In my testing, I saw query response times drop by about 180ms during peak traffic.
Implementing the change
You don't need to change your wp-config.php much, but you do need to be careful with how you handle the connection string. Here is the configuration flow I used:
- Install ProxySQL (version 2.4.x is stable).
- Define the backend server in the ProxySQL admin interface:
SQL
INSERT INTO mysql_servers (hostname, port, hostgroup_id) VALUES ('db_host', 3306, 10); LOAD MYSQL SERVERS TO RUNTIME; - Configure the user mapping so WordPress can authenticate through the proxy:
SQL
INSERT INTO mysql_users (username, password, default_hostgroup) VALUES ('wp_user', 'secure_password', 10); LOAD MYSQL USERS TO RUNTIME;
After that, update your DB_HOST in wp-config.php to point to the proxy:
PHPdefine('DB_HOST', '127.0.0.1:6033');
Trade-offs and gotchas
It wasn't all smooth sailing. We first tried using a simple HAProxy configuration, but it lacked the query-level intelligence we needed. HAProxy doesn't understand the MySQL protocol, so it couldn't perform read/write splitting. If you're looking for more advanced setups, I’ve previously discussed WordPress performance: Database proxy strategies for high concurrency to help with read-through caching.
Another trap: Prepared statements. ProxySQL handles them differently than raw PHP drivers. If your plugin uses complex, dynamic SQL, you might need to enable mysql-multiplexing carefully. I had to disable multiplexing for a specific session once because a custom SET @variable query was leaking into the next request's transaction scope.
If your plugin architecture is already complex, you might also consider WordPress sidecar architecture: Scaling plugins for high concurrency to offload non-database tasks, which keeps your DB connections focused strictly on data retrieval.
Is this right for every plugin?
Honestly, no. If you're building a plugin for standard shared hosting, don't touch this. ProxySQL adds a layer of infrastructure complexity that most users won't be able to manage. However, if you are architecting for database scalability in enterprise or high-concurrency environments, it's a necessary tool in your kit.
I'm still tinkering with the connection timeout settings. Too short, and you lose the benefit of the pool; too long, and you risk hitting the wait_timeout on the MySQL backend. It's a balancing act that requires monitoring your Threads_connected and Threads_running metrics closely.
Frequently Asked Questions
Does ProxySQL work with standard WordPress caching plugins? Yes. Since the proxy sits below the application layer, it’s completely transparent to plugins like Redis Object Cache or WP Rocket.
Can I use this on a single-server setup?
Yes, but you’ll want to ensure ProxySQL isn't competing for the same CPU cores as your MySQL process. Use taskset to pin them if necessary.
How do I monitor if it's actually working?
Use the stats_mysql_connection_pool table in the ProxySQL admin interface. If UsedConnections is consistently high, your pool size needs adjustment.
Next time, I want to explore how to integrate this with read/write splitting using a primary-replica cluster. For now, keep an eye on your connection spikes—they are usually the first sign of architectural debt.