As I mentioned in the comments, I see no reason to believe MySQL is the bottleneck here, but I do think the webserver configuration can cause this.
Installing a regular LAMP setup done by for example
apt-get install lamp-server^, will install PHP as an Apache module and pull in the prefork MPM. By default, this only starts with a limited number of workers (forks) to handle incoming connections. Any new connections will simply not be processed (though not refused). So, I think what you're seeing is simply that the "pool" of workers is to small to handle all the connections.
An error similar to this one will probably be in your Apache error logs somewhere when hitting this limit:
[error] server reached MaxClients setting, consider raising the MaxClients setting
To configure the prefork MPM in Apache, you can see the current values in
# prefork MPM
# StartServers: number of server processes to start
# MinSpareServers: minimum number of server processes which are kept spare
# MaxSpareServers: maximum number of server processes which are kept spare
# MaxClients: maximum number of server processes allowed to start
# MaxRequestsPerChild: maximum number of requests a server process serves
In order to change this, I'd recommend to create a new file in
prefork-mpm-tuning.conf and just "overload" the settings from their defaults (and restart Apache):
StartServers 20 # |-- important to increase
MinSpareServers 20 # |
MaxClients 70 # too high will get you out-of-memory here
Important note here is that Apache only scales up per second, starting with only one (hardcoded, not configurable) and never scales down unless you explicitly configure it to. As explained in the Apache documentation on performance tuning:
If the machine is busy spawning children it can't service requests. But it has such a drastic effect on the perceived performance of Apache that it had to be replaced. As of Apache 1.3, the code will relax the one-per-second rule. It will spawn one, wait a second, then spawn two, wait a second, then spawn four, and it will continue exponentially until it is spawning 32 children per second. It will stop whenever it satisfies the MinSpareServers setting.
This appears to be responsive enough that it's almost unnecessary to twiddle the MinSpareServers, MaxSpareServers and StartServers knobs. When more than 4 children are spawned per second, a message will be emitted to the ErrorLog. If you see a lot of these errors then consider tuning these settings. Use the mod_status output as a guide.
While the last paragraph may apply to general cases, I find it very confusing in testing that Apache seems unresponsive after restarting. For this reason my recommendation is to raise the
So, to summarize:
- You probably want to increase the
StartServers to accept all the subsequent connections you fire from the clients. Apache's prefork MPM is not very dynamic worker - to be able to have it perform well with a lot of simultaneous connections, you'll need to simply start a lot.
- Allow it to allocate enough memory (roughly 20MB per fork in practice). Yes you'll need loads of it just to serve 100 concurrent visitors in this design. (look at PHP-FPM if you need more performance with less memory)
- Limit the
MaxClients to prevent Apache to claim all memory of the server and crash it. Do math and testing here.
StartServers too low to save memory while maintaining a larger
MaxClients is a bad thing: your site will appear to be slow for the one-per-second reason and memory usage will grow later anyway. This will happen then.