data consistence between cache and database — cache aside

One question that may be frequently asked during an interview is that how you ensure data consistence between cache and database in an application. In this post, I will introduce some possible solutions and their drawbacks. While some of them are easy to implement, the others are not.

However, before we dive into the details, I want to address that it is really hard or impossible to implement real-time or strong consistence between cache and database. If you want to achieve this goal, locks must be used when reading or updating data in cache and database. But this will conflict the purpose of including cache in our application to improve performance.

Depending on the algorithms we used, there will always a time gap, either long or short, before the state of cache and database is synchronised. As long as business rules tolerate the time gap and the state of cache and database can eventually be.the same after the time gap, the algorithm will be applicable in practical projects.

Put it in another way, the data consistence between cache and database is not real-time. They will reach consistence eventually after some time. This is sometimes called eventual consistence.

Common solutions for the consistence between cache and database are cache aside, read through, write through, write behind, double delete. Let’s describe one by one.

1. Cache Aside

The algorithm is as below:

For read operation, if cache hit, process uses data directly from cache without contacting database. If cache miss, process reads data from database and updates cache.

For update operation, process updates data in database and then delete corresponding data in cache. The cache will be updated when another read operation occurs.

Under normal cases, this algorithm can ensure data consistence most of time. However, under exceptional cases, the consistence can be broken. Take the below flow for example:

Process A wants to update an entity. After it updates the data in database, it is killed due to some unknown reason before it deletes the data in cache. This will cause subsequent read for the entity to be stale until next update operation.

Under normal cases, this algorithm can still break data consistence in one corner case. Take the below flow for example:

Process A tries to read an entity in cache, but gets a cache miss. Then it reads the data from database. However, before it updates cache, process A is suspended or pause by OS process schedule algorithm.

Process B gets its CPU slot and tries to update the same entity. It updates database and deletes corresponding entry in cache.

After that, process A gets its CPU slot and updates the entry in cache. However, this entry in cache updated by process A is stale.

The data consistence between cache and database is broken until next update operation.

Even if this corner case may happen, the probability is relatively low. This happens only when process A gets a cache miss.

For cold data, it is usually rare for one process reading and another process writing at the same time, while for hot data, it usually resides in cache, so process A usually will get a cache hit.

Cache aside is the de facto standard in practice. In fact, there is an extended or enhanced cache aside algorithm called double delete. I will describe it along with other algorithms in later post.

Finally let me summarise the conclusion:

It is relatively hard or impossible to implement real-time or strong consistence between cache and database. Strong consistence requires using locks when reading or updating data in cache and database, which conflicts the purpose of using cache to improve performance.

Only eventual consistence can be ensured by using some well-designed algorithm. That means even if cache and database may not be consistent at some point in time, they will be consistent eventually after some period of time.

What’s more, business rules in your application must tolerate the short period of data inconsistent between cache and database.