Introduction
When dozens—or even thousands—of people use a website at the same time, the server needs to juggle all those requests smoothly. This is what we call concurrency. In a PHP/MySQL stack, poor handling of concurrency often shows up as slow page loads, failed transactions, or even complete outages.
Why Concurrency Matters in PHP/MySQL Stacks
PHP-FPM handles incoming requests, while MySQL powers the database queries behind each action. If either gets overwhelmed, the entire application stalls. A single unoptimised setting can become a bottleneck—forcing users to wait in line, much like too few cashiers at a busy store.
At small scale, these issues may not appear. But as traffic grows—think flash sales, peak booking hours, or viral campaigns—every second counts. Tuning PHP-FPM and MySQL for concurrency ensures faster responses, fewer crashes, and a smoother experience for end users.
PHP-FPM Tuning
Set the Correct pm Mode
PHP-FPM controls how it spawns workers with the pm (process manager) setting.
- Dynamic – best for high-traffic apps; keeps some workers always ready and scales up as load increases.
- Ondemand – good for lighter or bursty workloads; saves memory by only creating workers when needed.
Tweak max_children and max_requests
- pm.max_children sets the maximum number of worker processes. Too low = long queues; too high = memory exhaustion. Calculate it by dividing available RAM by the average memory each PHP process consumes.
- pm.max_requests recycles workers after serving a set number of requests (e.g., 500–1000). This prevents memory leaks from dragging down long-running servers.
Reduce Idle Process Load
Excess idle workers waste memory. Use:
- pm.min_spare_servers and pm.max_spare_servers (in dynamic mode) to keep just enough idle workers ready.
- Lower these values on memory-constrained servers, especially when many workers sit idle during off-peak hours.
Optimising these few parameters can make PHP-FPM far more stable under heavy concurrency.
MySQL Optimisation
Increase innodb_buffer_pool_size
This is the most important MySQL setting for performance. The buffer pool is where MySQL caches data and indexes in memory. Setting it too small forces the server to fetch from disk repeatedly—slow under load. A good rule is to allocate 60–70% of available RAM (on a dedicated DB server) to the buffer pool.
Limit Slow Queries
Even with enough memory, poorly written queries can block other requests. Use tools like EXPLAIN to analyse queries and add indexes where needed. Monitor the slow query log to catch inefficient SQL before it becomes a bottleneck.
Use Connection Pooling
Opening a new database connection for every request is expensive. Instead, use a pooling tool (e.g., ProxySQL, PHP’s pdo_mysql with pooling support, or external services) to keep connections open and reuse them. This reduces overhead and helps the database handle spikes in concurrent traffic.
System Monitoring
Use the Right Tools
- htop – real-time view of CPU and memory usage; helps spot overloaded servers.
- mysqltuner – gives practical recommendations for MySQL configuration based on workload.
- PHP-FPM status page (pm_status) – shows active, idle, and queued PHP processes in real time.
Watch Key Metrics
- Memory & CPU: Ensure PHP workers and MySQL aren’t consuming more than the server can handle.
- Query times: Track long-running queries; even one bad query can block hundreds of concurrent requests.
- Request queues: A growing queue in PHP-FPM means you need more workers or further optimisation.
Regular monitoring lets you adjust settings before problems cascade into downtime.
Scaling Tips
Separate App and DB Layers
Running PHP-FPM and MySQL on the same server works for small projects, but at scale it creates competition for memory and CPU. Splitting them onto dedicated servers (or containers) ensures each can use resources fully and makes troubleshooting easier.
Add a Load Balancer for PHP Workers
When one server can’t handle all incoming requests, distribute the load across multiple PHP-FPM servers. A load balancer (e.g., Nginx, HAProxy) spreads traffic evenly, improving responsiveness and providing redundancy if one server fails.
Scaling horizontally—adding more servers instead of overloading one—keeps performance stable as traffic grows.
Conclusion
High-concurrency applications live or die by how well they manage PHP-FPM and MySQL under pressure. Default settings may be fine for a small blog, but they quickly buckle when traffic spikes. By tuning key parameters, monitoring actively, and planning for scale, you build an environment that stays fast and reliable—even under thousands of simultaneous requests.
Production-Ready Checklist
- Set the right PHP-FPM pm mode (dynamic for high traffic).
- Calculate and tune pm.max_children based on available RAM.
- Use pm.max_requests and request_terminate_timeout to keep workers healthy.
- Allocate 60–70% RAM to innodb_buffer_pool_size.
- Monitor and fix slow queries; add indexes where needed.
- Implement database connection pooling.
- Track system health with htop, mysqltuner, and pm_status.
- Separate app and DB layers; add load balancer for scaling.
With these steps in place, your PHP/MySQL stack is optimised for concurrency and ready to handle production workloads with confidence.