As long as we use the cache, we will inevitably face the consistency problem between the cache and the database. If the data in the cache is inconsistent with the data in the database, the data read by the business application from the cache is not the latest data, and the impact on the business can be imagined. For example, we store the inventory data of products in the cache. If the inventory data in the cache is incorrect, it may affect the ordering operation, which is difficult to accept in business. In this article, let's talk about cache consistency.

How to fix cache inconsistency

Delete the cache first and then update the database

Suppose that after thread A deletes the cache, it has not had time to update the database. At this time, thread B starts to read data. When thread B finds that the cache is missing, it can only read the database. I just started to update the database. At this time, the data in the cache is the old value, and the database is the latest value, and the two are already inconsistent.

The solution to this scenario is to let thread A sleep for a short period of time after updating the value of the database, and then perform a cache deletion operation. The reason for adding a period of sleep is to allow thread B to start from The database reads out the data and then plugs the cached data back into the cache, and then thread A deletes it. Therefore, the sleep time of thread A needs to be greater than the time it takes for thread B to read data and then write it to the cache. How long is this time? This requires us to add management monitoring to the business for statistics, and estimate the time based on this statistical value. In this way, when other threads read the data, they will find that the cache is missing and will read the latest value from the database. We call this model "delayed double deletion".

Update the database first and then delete the cache

If thread A updates the value in the database, but has not had time to delete the value in the cache, thread B starts to read the data at this time. At this time, when thread B queries the cache and hits the cache, it will directly use the value in the cache , which is the old value. However, in this scenario, if the number of concurrent requests is not high, basically no thread will read the old value, and after thread A updates the database, deleting the cache is a very fast operation, so this situation is generally correct Small business impact. Generally, in a production environment, this mode is also recommended for everyone.

retry mechanism

You can put the cached value to be deleted or the value of the database to be updated into the message queue. When the application fails to successfully delete the cache or update the value of the database, these values can be consumed from the message queue, where the message queue is consumed The service is called job, and then it is deleted or updated again, which acts as a bottom-up compensation to ensure eventual consistency.

If it can be successfully deleted or updated, these values need to be removed from the message queue to avoid repeated operations. At this time, we can also ensure that the database and cached data are consistent, otherwise, we need to retry again, if If the retries exceed a certain number of times or fail, it is generally necessary to record an error log or send an alarm notification.

concurrent read and write

First, thread A reads the cache in the first step. At this time, the cache is not hit. Since the cache aside mode is used, thread A will read the database in the second step. At this time, thread B updates the database. After updating the database The cache is updated through set cache, and finally the fifth step thread A also updates the cache with the value read from the database through set cache, but at this time the data in thread A is already dirty data, because the fourth and fifth steps Both set the cache, causing the written values to overwrite each other, and the order of operations is uncertain, resulting in cache inconsistency.

How to solve this problem? In fact, it is very simple. We only need to replace the set cache operation in the fifth step with add cache. Add cache is the setnx operation. Only when the cache does not exist will it be successfully written, which is equivalent to adding a priority, that is, updating The update cache after the database has a higher priority, while the cache backlog after reading the database has a lower priority, so as to ensure that the latest data of the write operation will not be overwritten by the backlog data of the read operation.

concluding remarks

This article explains one of the most common problems encountered when using cache, that is, the problem of inconsistency between the cache and the database. For this problem, we list some scenarios that may lead to inconsistency and solutions to the corresponding scenarios. In particular, for job asynchronous In compensation scenarios, we can use the set operation to forcibly overwrite the cache to ensure that the update of the cache is the latest data. For the operation of reading the database back to the cache, we generally use add to update the cache.

Hope this article is helpful to you, thank you.

Updated every Monday and Thursday

Code repository: https://github.com/zhoushuguang/lebron

project address

https://github.com/zeromicro/go-zero

Welcome go-zero and star support us!

WeChat exchange group

Follow the official account of " Microservice Practice " and click on the exchange group to get the QR code of the community group.


kevinwan
931 声望3.5k 粉丝

go-zero作者