Optimizing server configuration for PHP applications

Posted 17:16, 23/4/2009, in Web

We recently migrated all of our websites to some new servers, and for the first time we have some dedicated servers for our PHP CMS platform. This gave me the opportunity to fine tune configuration specifically for PHP, and I was surprised at how much difference could be made without even touching the application code.

The setup includes two web servers and a dedicated MySQL server, all with 1G of RAM, running CentOS. I was using Apache bench for the tests, with the parameters ab -c 10 -n 500 http://www.example.com/ (10 concurrent requests, 500 in total). The starting point was a standard yum-installed LAMP setup except for the addition of the Remi repositories for more recent PHP packages (PHP 5.2.9, MySQL 5.0.45, Apache 2.2.3).

If you're not familiar with Apache bench, the 'requests per second' stat is the thing to look out for in the results, higher is better. Each test was run after 'warming up' the caches, and there weren't any failed requests.

Firstly the baseline test (fresh after installation):

Requests per second:    13.31 [#/sec] (mean)
Time per request:       751.477 [ms] (mean)
Time per request:       75.148 [ms] (mean, across all concurrent requests)
Transfer rate:          43.27 [Kbytes/sec] received

Then after installing APC:

Requests per second:    18.52 [#/sec] (mean)
Time per request:       539.833 [ms] (mean)
Time per request:       53.983 [ms] (mean, across all concurrent requests)
Transfer rate:          60.24 [Kbytes/sec] received

Then started the tweaking. The Remi php-apc package has apc.shm_size set to 32M by default (slightly higher than the PHP default of 30M). As I understand it, this variable controls how much RAM is reserved by APC. If it is set too low, APC won't be able to cache all of your commonly included PHP files; but as you increase it, once you pass the point at which your whole app can be cached you'll start to see a reduction in performance, since you're reducing the amount of memory that can be used by Apache. After some experimentation I kept this at 32M.

Next I turned off apc.stat. With this option enabled APC checks the last modified time of each included PHP script on each request to see if it needs to be recompiled. With it off you save quite a lot of file system calls, especially if you have a lot of file includes, but it means you need to restart Apache after any code changes. Results:

Requests per second:    18.89 [#/sec] (mean)
Time per request:       529.346 [ms] (mean)
Time per request:       52.935 [ms] (mean, across all concurrent requests)
Transfer rate:          61.43 [Kbytes/sec] received

Not much difference! The application code in this case is stored on a shared NFS drive, so perhaps the local NFS client is caching some of the file information.

Next I enabled apc.include_once_override, which gets around the problem APC used to have caching files included using include_once or require_once:

Requests per second:    23.00 [#/sec] (mean)
Time per request:       434.698 [ms] (mean)
Time per request:       43.470 [ms] (mean, across all concurrent requests)
Transfer rate:          74.81 [Kbytes/sec] received

If your application uses Zend Framework you'll probably see even more benefit here, since ZF uses include_once extensively.

Next came the MySQL tweaking. Although MySQL comes with some suggested configuration files for different setups, the defaults are quite conservative. If you're not familiar with the different MySQL config options, I'd suggest installing MySQLTuner, a free script you can run which will analyse your installation and suggest some changes.

By far the biggest improvements you can make are enabling the query cache and increasing key_buffer_size. If you're running a dedicated server for MySQL you can assign a decent chunk of RAM to these two. The settings I used:

key_buffer_size=128M
query_cache_size=256M
query_cache_limit=16M
thread_cache_size=4
table_cache=128

Results:

Requests per second:    61.60 [#/sec] (mean)
Time per request:       162.339 [ms] (mean)
Time per request:       16.234 [ms] (mean, across all concurrent requests)
Transfer rate:          199.42 [Kbytes/sec] received

Big improvement!

Obviously there are a number of application-level things that can be done to improve performance further (page/fragment caching, caching objects in APC/memcache), but this shows how much improvement you can gain just by tweaking some config files.

Add comment

Or login with OpenID

Search this site
Login
(or login/signup the old fashioned way)
Elsewhere

External URLs/articles that may be of interest:

Adobe releases 64-bit flash player for Linux

Adobe have released an alpha of their 64-bit flash player for Linux (I believe this is the first 64-bit player they've done on any platform, it's not available for Windows or Mac yet). And it works great. It may be an alpha but so far I've found it considerably more stable than the released 32-bit wrapper version. It also uses considerably less CPU power. Easy installation instructions via. this blog entry.

MS Office in the browser

The most interesting thing about this announcement is that MS say the system will work in Firefox and Safari as well as IE. The Microsoft of 10 years ago would have used proprietary IE only code and used it as a way to leverage IE market share.

The Future of Advertising, Branding, Media and Communications

Video of a very interesting talk by Gerd Leonhard on the future of the media industry, and content on the Internet.

Future of web browsing from Mozilla Labs

Some interesting ideas about how web browsing might look in the future (watch the first video).