Last time, we covered how to optimize the application to avoid calls to the database. A cache can offer a solution when you can neither avoid getting the data nor make the queries more efficient.

A cache serves as a logical middle layer between the application and the database.

A general pattern I’ve seen for caching:

KEY="data"
data = cache.get( KEY )
if (!data) {
data = db.query("slow")
cache.set(KEY, data, 30);
}

You’ll notice that the code first checks the cache to see if we get a “hit”. If not, we query the database and store the data back to the cache.

Now we know how to store and retrieve data in the cache.

What happens when a user sees the data, changes something, and then wonders why the data isn’t showing up?

Many cache services like Memcache and/or APC allow for a timeout to be specified so that we can make the data we put into the cache expire automatically.

Timeouts only go so far. Sometimes you need to lower the timeout to give the user fresh data, which marginalizes the benefit of caching. Ideally, your application knows exactly when the cache becomes invalid–when the user updates the object affected by the cache. A solid caching strategy also dirties the cache elements so the user never has knowledge of the cache!

We do lots of caching here to keep iContact running. We run a cluster of Memcache servers to store our sessions, and cache lots of data that’s frequently accessed to reduce the load on our database servers. On some pages, once the cache is warmed the number of queries goes down to 2-3 per page!