Mike Willbanks recently wrote a good article about performance tuning. There’s some good advice in there, and I thought it’d be a good idea to quickly add a bit more detail about the separate approaches that Mike raises.
Mike recommended using APC for bytecode caching. APC’s pretty good, but just be aware that APC isn’t compatible with Sara’s excellent runkit extension. Xcache is, but some versions of Zend Optimizer refuse to run if they detect Xcache has been loaded. (Btw, Zend Optimizer is worth looking at, but because of the way Zend compile it, it can affect overall scalability. I haven’t sat down yet and worked out whether Zend Optimizer’s performance improvements make up for the cost of how the Linux kernel has to load it into Apache. I touched on the issues with how things are compiled last year, but haven’t followed it up yet with any definitive figures on scalability.)
If you don’t need Zend Platform’s download server (which rocks), then XCache + Zend Optimizer + Memcache out-performs Zend Platform substantially, and costs a lot less too ;) Zend Platform also isn’t compatible with runkit. It’d be great to see runkit supported better by accelerators.
Memcache is best suited to storing smaller pieces of data. If you’re using it to cache whole XHTML pages, they sometimes don’t fit into Memcache, and need to be cached on disk instead. (Always cache onto local disk, never NFS). Memcache divides the memory allocated to it into different size buckets for performance reasons, and there are far more small buckets than there are large buckets. You can edit the Memcache source code and change the size of the largest bucket before recompiling.
The GZIP trick Mike mentions just isn’t safe with IE6. There are copies of IE out there that fail to decrypt the content correctly alas :( I remember reading a stat that it was about 1% of copies of IE had this bug, but I don’t have the link to hand. I have seen copies of IE with this bug myself. There’s nothing more frustrating than looking at two copies of IE, both reporting exact version numbers, and one copes with GZIPed data whilst the other one doesn’t :( It’s possible that the widespread adoption of IE7 has “fixed” a lot of these buggy IE copies.
I’d recommend placing more emphasis on the Not Modified header, and also on making sure that your code is architected to send back Not Modified headers as quickly as possible. It not only improves per-page performance, but reduces per-page memory usage, and substantially improves scalability. Getting this right can make a huge difference, especially for sites where users normally view more than one page per visit. And make sure the metadata you use to work out whether or not you can send back the Not Modified header is fine-grained enough :)
Also, looking at the Not Modified header … don’t take it for granted that Apache is getting this right for your static files. I can’t remember which Apache module disables this off the top of my head (I think it was mod_includes, but I could be wrong), but check the HTTP traffic to make sure your site isn’t sending static files when it doesn’t need to.
With SQL queries of the form “SELECT … FROM table WHERE primaryKey IN ( … )”, be aware that the size of the IN list varies from database server to database server, and it doesn’t take all that big a list before you run into portability problems.
One important thing Mike didn’t touch on was about separating out static files onto a separate box. Apache + mod_php doesn’t serve static files very efficiently. With static files on a separate box, you can recompile Apache to use the “worker” MPM, which serves static files substantially better, or you can use an alternative web server such as lighttpd.
There are plenty of other things you can do to optimise PHP on servers, such as tuning Apache to prevent swapping, tuning the Linux TCP/IP stack to reduce connection failures at peak times, and moving your database off onto a separate box. I’m going to go into these in a lot more detail at a later date.
Finally, xdebug is a fantastic tool for profiling your code and telling you where you have inefficient loops and whatnot. It takes the guesswork out of finding bottlenecks!
15 comments »
The challenge with securing a shared hosting server is how to secure the website from attack both from the outside and from the inside. PHP has built-in features to help, but ultimately it s the wrong place to address the problem. Apache has built-in features too, but the performance cost of these features is prohibitive.
This has created a gap that a number of third-party solutions have attempted to fill. One of the oldest of these is suphp, created by Sebastian Marsching. How well does it work, and how well does it perform?
- suphp: Running PHP As A Specified User
- Installing suphp
- Configuring Apache
- Some Benchmarks
- Other Considerations
suphp: Running PHP As A Specified User
Like Apache’s own suexec, suphp is a solution that allows PHP to run as the user and group that owns any particular website on a shared hosting server.
suphp consists of two components:
- mod_suphp, an Apache module that replaces mod_php
- suphp, a setuid binary that replaces Apache’s suexec
It relies on PHP/CGI having been installed onto the server first.
suphp is compiled and installed in the same way as any other Apache module. These instructions are for Apache 2.2, but they will work fine for Apache 2.0 as well. (You can run suphp on Apache 1.3 too). (Gentoo Linux and Seed Linux users can skip these instructions; you can install suphp using ‘emerge mod_suphp‘).
Download the suphp source code from the website. Unpack the tarball somewhere, like this:
tar -zxf suphp-0.6.2.tar.gz
Next, run the configure script:
You’ll need to adjust some of these settings to suit your local operating system:
- –with-min-uid sets the lowest user id that PHP is allowed to run as. Check your /etc/passwd file to see just how low this needs to be set.
- –with-min-gid sets the lowest group id that PHP is allowed to run as. Check your /etc/group file to see just how low this needs to be set.
- –with-apache-user tells suphp which user Apache will be running as.
- –with-logfile tells suphp where to write log messages to. I recommend that you configure suphp to write its logfile in the same directory that Apache would normally write its log files.
- –with-apxs tells suphp where to find the Apache util that’s used to help build Apache modules.
- –with-apr tells suphp where to find the Apache Portable Runtime (apr) config util.
It’s worth pointing out that there are several different options to choose from for the –with-setid-mode config. Check out the suphp documentation (included in the source tarball) for the details on what each mode does.
Next, run make to compile the software.
Assuming everything compiles just fine, the next step is to install mod_suphp by copying it to your Apache modules directory:
cp src/apache2/.libs/mod_suphp.so /usr/lib/apache2/modules/
After that, you need to install the suphp binary:
cp src/suphp /usr/sbin/suphp
chmod 4755 /usr/sbin/suphp
mod_suphp is hard-coded to expect the suphp binary to be installed into /usr/sbin. If you put it anywhere else, mod_suphp won’t be able to run PHP for you!
To finish, download the suphp configuration file from Gentoo’s CVS, and install it as /etc/suphp.conf. Then, edit the file, updating all the settings to match the values you passed to the configure script.
The first thing you need to do to your Apache config files is to comment out mod_php. mod_suphp is a replacement for mod_php; you cannot run both at the same time.
Then, in your main httpd.conf file, add the following:
LoadModule suphp_module modules/mod_suphp.so
AddType application/x-httpd-php .php
AddHandler application/x-httpd-php .php
DirectoryIndex index.php index.htm index.html default.htm default.html
This tells Apache to load mod_suphp, to associate it with PHP scripts, and to look for index.php et al when a URL only specifies a folder name instead of a file.
The final step is to go into each of your virtual hosts, and tell mod_suphp which user owns the virtual host. This will the user and group that PHP will run as.
SuPHP_UserGroup stuart mybusiness
Now you’re ready to restart Apache and to run some tests to make sure that suphp is up and running.
suphp works the same way as Apache’s suexec. Every time a PHP script is run, suphp has to fork Apache and then execute another copy of the PHP/CGI binary. This approach provides the absolute security benefits that we seek, but is much slower than using mod_php.
To benchmark suphp, I used Apache’s ab benchmark to load a simple phpinfo() page 1,000 times. I ran the benchmark five times, and averaged the results.
- suphp: average of 164.677 seconds
- mod_php: average of 6.422 seconds
suphp is some 25 times slower than mod_php. This is a substantial performance hit, but it’s better than suexec, which benchmarked at 36 times slower than mod_php. I admit to being surprised that suphp performs better than suexec; I plan to put all of the alternatives covered here in a “head to head” article soon!
One neat feature of suphp is that it can support both PHP 4 and PHP 5 running on the same box at the same time. Hopefully, you’ve already made the move to PHP 5 and you don’t need this feature – but it’s there if you do.
The same feature can be used to support both PHP 5 and PHP 6 (when it’s released) at the same time.
Be aware that the last release of suphp was in 2006. There is an active mailing list you can join for community help and support.
suphp is an easy-to-install, easy-to-configure and easy-to-maintain alternative to Apache’s own suexec. If you are running a shared server and the horrific performance penalty doesn’t put you off, then suphp is well worth looking at instead of using suexec.
But the question is – is there anything better out there, something that provides both security and performance? In the next article, I’ll take a look at a third-party Apache mod that attempts to answer that.
This article is part of The Web Platform, an on-going series of blog posts about the environment that you need to create and nurture to run your web-based application in. If you have any topics that you d like to see covered in future articles, please leave them in the comments on this page.
26 comments »