I'm getting to know far more about servers than I ever wanted to, after hundreds of hours of Google research trying to squeeze/beat performance out of Apache. I do have 15 years programming experience in other areas, and I've reached the conclusion that the only experts in Apache/Linux are the programmers who wrote (and poorly documented) all this stuff. So I've gathered everything I could learn after countless hours of frustration and I'm writing this up in return for the immense amount of help I've received from the documentation of others.
If you're reaching the limits of your Apache server because you're serving a lot of dynamic content, you can either spend thousands on new equipment or reduce bloat to increase your server capacity from 2 to 10 times. This article concentrates on important and weakly documented ways of increasing capacity without the need for additional hardware.
There are a few common areas of server load problems, and a thousand uncommon. Let's focus on the top three I've seen:
Apache processes use a ton of RAM. This issue becomes major when you realize that after each process has done its job, the bloated process sits and spoon-feeds data to the client, instead of moving on to bigger and better things. This problem is compounded by a bit of essential info that should really be more common knowledge:
If you serve 100% static files with Apache, each
will use around 2-3 megs of RAM.
If you serve 99% static files and 1% dynamic files with Apache, each
httpd process will use from 3-20 megs of RAM (depending on your
MOST complex dynamic page).
This occurs because a process grows to accommodate whatever it is serving, and NEVER decreases until that process dies. Unless you have very few dynamic pages and major traffic fluctuation, most of your httpd processes will soon take up an amount of RAM equal to the largest dynamic script on your system. A very smart web server would deal with this automatically. As it is, you have a few options to manually improve RAM usage.
This is a tradeoff. The
KeepAliveTimeout configuration setting
adjusts the amount of time a process sits around doing nothing but taking up
space. Those seconds add up in a huge way. Using
increase speed for both you and the client — disable it and the serving of static files such as images may be a lot slower. I think it's best to have
KeepAlive on, and
KeepAliveTimeout very low, perhaps
one or two seconds.
If you use Apache to serve dynamic content, your simultaneous connections
are severely limited. Exceed a certain number, and your system begins
cannibalistic swapping, getting slower and slower until it dies. Personally, I
think the system and/or web server should automatically take steps to prevent
this, but by default, they tend to allow the server to consume itself. Use
trial and error to figure out how many Apache processes your server can handle,
and set this value in
Note: the Apache docs on this are misleading — if this limit is
reached, clients are not "locked out," they are simply queued, and their access
slows. Based on the value of
MaxClients, you can estimate the
values you need for
Forcing your processes to die after awhile makes them start over with lower
RAM usage. This can reduce total memory usage in many situations. The less
dynamic content you have, the more useful this will be. This is a game of
catch-up, with your dynamic files constantly increasing total RAM usage, and
restarting processes constantly reducing it. Experiment with
MaxRequestsPerChild (even values as low as 20 may work well), but don't set it too low, because creating new processes does have
You can figure out the best settings under-load by examining
--sort:rss. A word of warning: the results can be impressive, but are NOT consistent. If the only way you can
keep your server running is by tweaking this, you will eventually run into
trouble. That being said, by tweaking
may be able to increase
MaxClients as much as 50%.
Finally, think outside the box: replace or supplement Apache.
You can use a tiny, lightning fast server to handle static documents and images, and pass more complicated requests on to Apache on the same machine. This way Apache won't tie up multi-megabyte processes serving simple streams of bytes. You can bring Apache into play, for example, only to execute PHP scripts. Good options for this include:
Lingerd takes over the job of feeding bytes to the client after Apache has fetched the document, but requires kernel modification. It sounds pretty good, but I haven't tried it.
A proxy cache can keep a duplicate copy of everything it gets from Apache, and serve the copy instead of bothering Apache with it. This has the benefit of also being able to cache dynamically generated pages, speeding up requests at the expense of using more memory.
You could custom build Apache, with optimal settings for your situation, removing parts that you don't require. This is not for the faint of heart.
If you don't need all the features of Apache, replace it with something more scalable. Currently, the best options appear to be servers that use a nonblocking I/O technology and connect to all clients with the same process. The best include:
Compiling PHP scripts is usually more expensive than running them. Why not use a simple tool that keeps them precompiled? Options include Turck MMCache (fast and free but buggy), PHP Accelerator, APC, and Zend Accelerator. You will likely see a speed increase of 2 to 10 times and a PHP RAM reduction of 50%-85%.
See Optimizing PHP for more ideas.
This is covered in detail everywhere, so just keep in mind a few important
points. One bad query statement running often can bring your site to its
knees. Two or three bad query statements don't perform much differently than
one. In other words, if you optimize one query you may not see any server-wide
speed improvement. If you find and optimize ALL your bad queries you may
suddenly see a fivefold server-speed improvement. The
log-slow-queries feature of MySQL can be very helpful.
If you don't need all the extra features of a relational database, storing data with your own method can be many times faster than MySQL. For temporary data, store it in each user session, or globally with a shared memory library (like the one included with Turck MMCache). More important data can be stored in files, or in your own flat-file database system. A combination of both could be very powerful — your own flat-file database with a copy cached in memory, going to disk only on write.
Here are a few other things you may find useful.
ps axu to check for processes that are
using too much CPU or RAM. But beware, these programs will actually lie to you.
The total RAM used doesn't always match up — an application's threads
share the same memory pages, though it may not look that way.
netstat -anp | sort -u to check for network problems.
ab to benchmark your results — but keep
in mind this tool doesn't accurately simulate actual usage, most notably the
effect of many dialup users, who keep connections open longer than you'd
If it makes you ill to think about a 20-meg process spending 15 seconds
waiting on a
KeepAlive after serving a 2k JPEG file, you're not
alone! I come from the days of 1 MHz machines running multi-line BBSs. The
idea that gigahertz machines with gigabytes of RAM are being limited to 200
simultaneous connections suggests that we're far more influenced by a certain mega-corporate mindset than we'd like to believe. With some basic knowledge of
the software and a few of the tips here, you can decrease the bloat and get
your server running at least within a reasonable range of how it should,
without investing in more hardware.
Adam Pedersen is a freelance web programmer and game developer.
Return to ONLamp.com.
Copyright © 2009 O'Reilly Media, Inc.