Wordpress has become one of the most popular blogging engines on the Internet, and has numerous features that make blogging simple and easy. While there are numerous upsides to using wordpress, there is also one major drawback. Wordpress generates each blog entry dynamically from a database, which can cause a considerable drain on computing resources. To get a better idea of just many resources wordpress would consume on a 2GHZ/1GB AMD Athlon server, I decided to run a few tests with the siege web benchmarking utility. The first test I ran was a 50-user simulation:
$ siege -c 50 http://prefetch.net/blog
Availability: 100.00 %
Elapsed time: 50.04 secs
Data transferred: 4.28 MB
Response time: 10.05 secs
Transaction rate: 4.00 trans/sec
Throughput: 0.09 MB/sec
Concurrency: 40.17
Successful transactions: 250
Failed transactions: 0
Longest transaction: 31.60
Shortest transaction: 0.09
Ouch! The results reported by siege were nothing like I expected. When 50 simultaneous users were accessing my website, it took over 10-seconds to render the main page of my blog. In addition, the server run queue was over 30, and the box was nearly unusable during the test (luckily I was using the Solaris fair share scheduler to limit how much CPU time Apache could consume). Since these results were unacceptable, I decided to start profiling and tuning Wordpress to serve up content quicker. After spending a bit of time reviewing the MySQL performance data, I adjusted several buffer cache values that were sized too low, and enabled the Wordpress post-query accelerator to allow the SQL queries to be served from the MySQL query cache. Once these changes were made, I ran siege again, and the results were not much better:
$ siege -c 50 http://prefetch.net/blog
Transactions: 268 hits
Availability: 100.00 %
Elapsed time: 62.02 secs
Data transferred: 6.25 MB
Response time: 9.84 secs
Transaction rate: 4.32 trans/sec
Throughput: 0.10 MB/sec
Concurrency: 42.50
Successful transactions: 317
Failed transactions: 0
Longest transaction: 32.78
Shortest transaction: 0.08
Since the machine was limited by CPU, I decided to fire up DTrace to see where the httpd processes were spending their time. The DTrace results indicated that Apache was spending a considerable amount of time compiling PHP pages, and issuing queries to the back-end database. Since I only update my blog a few times each week, I started to wonder if there was a way to turn the pages I created in Wordpress into static content. After reading through a variety of wordpress resources, I came across the wordpress cache #2 plugin. This super useful plugin will create a static represetnation of each dynamically generated page, and serve that file instead of the page that wordpress would dynamically generate. Once I got wp-cache2 installed, the results were right on par with what I orginally thought they should be (since I don’t typically serve 50 simultaneous user sessions, 2.76 seconds was sufficient):
$ siege -c50 http://prefetch.net/blog/
Lifting the server siege... done.
Transactions: 592 hits
Availability: 100.00 %
Elapsed time: 45.44 secs
Data transferred: 30.93 MB
Response time: 2.76 secs
Transaction rate: 13.03 trans/sec
Throughput: 0.68 MB/sec
Concurrency: 35.99
Successful transactions: 592
Failed transactions: 0
Longest transaction: 33.67
Shortest transaction: 0.24
Since the load was low processing 50 simultaneous users, I decided to see what 100 simultaneous sessions would look like:
$ siege -c100 http://prefetch.net/blog
Availability: 100.00 %
Elapsed time: 54.91 secs
Data transferred: 35.59 MB
Response time: 2.15 secs
Transaction rate: 24.15 trans/sec
Throughput: 0.65 MB/sec
Concurrency: 51.93
Successful transactions: 1423
Failed transactions: 0
Longest transaction: 34.29
Shortest transaction: 0.06
The results with 100 sessions were just as good (actually better) than the 50-session output, and I was able to get the results even lower by enabling the PHP cache accelerator. So in summary, if you are looking to handle large volumes of connections with wordpress, you might investigate the following:
Viva la blizog!