Thanks for the links. I plunged into a number of sites and after a few hours have emerged having determined that the primary problem is Apache HTTPD's default preforking settings.
I've fixed up my memory problems. Admin is faster but still unacceptably slow; I think I'll have to move on to MySQL optimisation next to fix that up. The good news is that blog pages are lightening fast now!
Here is some help for anyone else having problems with overrunning memory with WordPress...
How to fix up preforking on Apache HTTPD
In a Linux environment Apache HTTPD will prefork (ie create) up to 150 threads in advance to handle requests. Each of those threads appears to have a complete instance of WordPress code running in it; on my machine that requires around 40-45MB. That means 40-45MB of private memory per thread. Within a few minutes of startup Apache would have around fifteen threads running, and 15 ✕ 45MB = 675MB which is more RAM that I actually had.
However those threads are completely unnecessary on my small site. They're sitting there waiting for requests and over 99% of time they've got nothing to do. So I drastically reduced the limits, allowing only 10 threads to be preforked instead of 150.
My Apache HTTPD configuration now contains:
This has fixed my problem, dropping my peak memory usage to below 512MB.
How to keep those threads healthy
One problem with dropping the number of threads is that it limits my server to handling only 10 requests at once. But my site is small and I doubt that I'd hit this limit soon; and Apache queues up requests so spikes should be smoothed out.
Even so, better safe than sorry. A few things I picked up in my reading are:
- Use a webserver-level cache like WP Super Cache
- Reduce Apache's request timeout
- Reduce Apache's Keep-Alive timeout
Using WP Super Cache with the 'Use mod_rewrite to serve cache files' setting enabled means that Apache HTTPD serves up the pages directly. This means each thread completes its work very quickly, and thus will be ready for the next request more quickly.
Reducing the request and Keep-Alive timeouts in Apache HTTPD configuration is very important. By default the request timeout is five minutes, so if the browser doesn't acknowledge the response then that thread would not be available for the five minutes. Ten requests like that and your website disappears for a few minutes! I've dropped mine to 45 seconds.
The default Keep-Alive timeout is 15 seconds. This means that when Apache finishes handling a request from a browser that use Keep-Alive (which is most browsers) it waits 15 seconds for further requests before allowing someone else to use the thread. This could limit a thread to serving up only four pages a minute; reducing the KeepAliveTimeout setting to 3 seconds means a thread could handle more like twenty pages a minute.
These two posts really helped me: