Simple Yet Effective Page Caching with MySQL in WordPress

When dealing with high-traffic WordPress sites, caching is king, we all know that, and the most effective type is full page caching. There are so many different plugins, tools and utilities available for page caching in WordPress, it’s already hard to pick one, so to make it even harder, today we’re introducing Pressjitsu Page Cache.

Pressjitsu Page Cache (we have yet to come up with a more clever name) is a simple full-page caching plugin for WordPress. We developed it specifically for our hosting platform, but it works fairly well in other environments too.

Key Principles

Before jumping into writing code, we created a list of principles we wanted to follow with our new plugin. We didn’t want it to be the “yet another” type plugin, but we also didn’t want to follow the “omg this plugin has a billion features!” model, so here’s what we came up with.

  • Very fast and lightweight
  • MySQL for storage
  • Maximum compatibility
  • No configuration UIs
  • Fool-proof

We loved and used Automattic’s Batcache for many years. Our new plugin was inspired by Batcache, we even wanted to call it “Robin Cache” at some point. But Batcache has one major drawback — a dependancy on Memcached (or another persistent object store).

Persistent object caching is great and everything, but it’s only great in certain environments under certain conditions, where MySQL servers would quickly become a bottleneck. But until those conditions are met, Memcached is actually a bit slower, mainly because of all the extra round-trips a PHP process has to make to a Memcached server to retrieve the same data it could retrieve from MySQL with a single round-trip.

So the first version of our caching plugin was a Batcache fork minus all the object caching stuff, plus all the MySQL stuff.

Performance

A cached view with Pressjitsu Page Cache is served in 2-5 milliseconds on average.

Given that there’s a limited amount of data an InnoDB buffer pool can store in memory, MySQL will sometimes read from disk when accessing pages that haven’t been viewed recently, which is still very very fast, but not in the 2-5 ms range.

Under load, Pj Page Cache can degrade pretty significantly, depending on the server resources and configuration limits available to MySQL. For example, given an InnoDB buffer pool of 128M, and 150 maximum MySQL threads on a single-core server, we were able to handle over 7000 requests in one minute:

Loader.io Test Results
Loader.io Test Results

That’s with a steady increase from 0 to 100 requests per second. As you can see the average response time is about 400 milliseconds. This includes the time required to serve the data from cache, plus the HTTPS handshake, plus the time required to actually transfer 25kb of data.

During this test CPU usage peaked at 60% from an average of about 20%. We think that’s pretty amazing.

Installing

Installing and updating Pj Page Cache is a bit trickier than other WordPress plugins, but we’re pretty sure you can handle it:

  1. Extract the plugin into into wp-content/mu-plugins/pj-page-cache
  2. Copy the plugin’s advanced-cache.php file to your wp-content directory
  3. Place define( 'WP_CACHE', true ); in your wp-config.php file
  4. Visit your site’s Dashboard (main site’s Dashboard in a Multisite install)

As we mentioned, there’s no configuration UI. No checkboxes, dropdown selects, textareas, and especially no “Flush Cache” button! You’ll know when it’s working because the site will be blazing fast, or you can look at HTTP headers.

Some options, however, can be adjusted with a pj-cache-config.php file. We use this mainly to avoid cache killers, such as session cookies, ad-specific cookies, analytics data (utm_ variables), and others.

InnoDB vs MyISAM

Honestly we haven’t even tried running this with MyISAM, and we urge you not to. The reason is that high-frequency and concurrent updates with table-level locking in MyISAM are going to be a performance killer if not a deadlock.

Use InnoDB. And give the buffer pool some room to breathe.

Also make sure that the WordPress cron is running. Pj Page Cache will do its garbage collection every hour where it deletes cache entries that expired more than 24 hours ago. It’s important to remove these, otherwise you may end up filling your InnoDB tablespace, which is not easy to shrink back once it has grown.

Contributing

Pressjitsu Page Cache is available on GitHub under the GNU GPL. Feel free to report issues or open pull requests, or e-mail support@pressjitsu.com if you need any assistance.