Cache consistency resolution

1. Cache consistency problem

When we read all the data, we first look at the cache, and if there is no cache, we will read the database . If we read it for the first time and it is not in the cache, then, to check the database, it is a new value.

Now let's consider a problem. If the database modifies a piece of data, and the data is also in the cache, let's get the data from the cache again, which is a piece of old data .

Therefore, another issue is involved, the issue of cache data consistency, and how to keep the data in the cache consistent with the database .

To solve the problem of cache consistency, the two more commonly used modes are

1. Double write mode

2. Failure Mode

No matter how concurrency these two methods are, some loopholes will occur.

Two, double write mode

2.1 The idea of ​​double writing mode

First of all, we use the double-write mode, we have modified the database, for example, we modified a menu, and then the data in redis wants to change, after we have changed the data in the database, we will also change the data in the cache

2.2 Problems in dual write mode

Double write mode, our idea is to change the data in the cache after writing, suppose now we simulate two concurrent, all come in

For example, thread number 1 is executed first, and it changes the database first, and changes the data to 1. After it changes the data,

At this time, the second modification request is to modify this data. It wants to change this data to 2, which is equivalent to our last modification to the database and wants to change to 2.

However, if we use the dual-write mode, our first thread changes the database and needs to change the cache, but it may be due to various reasons. After it changes the database, the CPU does not go to it, or it freezes for a while. I want to write to the cache, but it doesn't run as fast as the second thread.

Thread 2, after changing the database, swish and change the cache to 2

But at this time, thread 1 slowly writes the data to the cache, which is changed to 1.

At this point, we saved the data as 1 in the cache, but in fact the latest data in our database is 2, not 1, which is equivalent to a piece of dirty data in our cache .

Therefore, we said that the dual-write mode has the risk of generating dirty data .

How to solve this problem?

2.2.1 Solution One

We can consider a design solution that is to lock . For example, when we have concurrent writes, because you want to change the database and the cache at the same time, then we will add a lock to the entire operation. If the number 1 comes first, then 1 No. 1 gets a lock. Only when No. 1 completes its entire process, No. 2 can get the lock and write about its entire flow.

This will not produce the data inconsistency problem we are talking about.

2.2.2 Solution Two

Seeing that our business does not allow our data to have temporary data inconsistencies . For example, the menu data of our product data has been changed. Maybe we have 5 minutes and 20 minutes, and the website display on our homepage is the data we changed. , Do we allow this kind of operation? If allowed, we can ignore this matter.

Whatever to do? That is, when we put the data in the cache, we gave each data an expiration time, such as one day. If the data expires, we will automatically delete it from redis. After deleting it, we will check the data next time. , It will check a new copy and put it in redis, so we can also call this a temporary dirty data problem, provided that we add an expiration time to the cache when designing.

Moreover, we finally found that no matter how we operate, we write to the database, write to the cache after finishing, update the cache, and then read the data. The latest data read must have a delay with the latest data stored in the database at that moment. , That is to say, this period of delay time, how much we tolerate , we tolerate 1ms, 1s or 1 day, no matter how we tolerate it, we call this final consistency .

It is the modified value of our database. To the last value we see, there is a relatively large delay between them, but no matter how the delay is, we will eventually see the latest modified value of the database.

Therefore, the consistency of our cached data is to fully satisfy our final consistency. No matter how we design, we ensure that the data read in the final cache is the latest data in the database .

This is a problem that is prone to occur in the dual-write mode we are talking about.

Three, failure mode

3.1 Failure mode thinking

The failure mode is that after we modify the database, we directly delete the cache. What are the benefits of doing this?

For example, we have changed the database, but we also cache all the data of our three-level classification in the cache, such as redis.del(key);

After deleting it, there is no data in the cache. Next time we check this menu, our query method will judge that there is no data in the cache, so we will actively check the database once.

3.2 Problems with failure modes

The failure mode also has the same problem . For example, let’s take an example. For example, after we update the data and the database is updated successfully, we will delete the cache again. After deleting the cache, there will be no data in the cache. Check the data again next time. There is no cache. , Will actively check the database, actively update the cache, automatically trigger the active update function, and finally get the latest data .

There is such a scenario below. Now there are three requests. The order of these three requests is like this. First, the first request is to change the menu data of No. 1. Change this data to 1. After the change, delete the cache. , And then the execution is over.

Next, the second request is to change our data to 2, but its machine may be slower and it takes a long time

Then came the third request

Now there is such a problem. When the first request is deleted from the cache, the second request comes in and reads, and there is indeed no data in the cache. Then it reads the database, but the database, at this time, the data No. 2 has not been changed. Did not submit the latest changes

That is equivalent to reading the old data 1 on the 3rd, and then after reading the 3rd, it needs to update the cache

This is another problem. If its update cache is faster, it is okay, it changes a wrong cache, and then our 2nd thread writes and deletes the cache again, which is equivalent to it has not been updated.

However, if it is updated slowly, the cache is deleted immediately after the update of the No. 2 machine, and the 1 read by the No. 3 machine before will be directly placed in the cache.

Therefore, in the end, our latest data 2 was not put in the cache, but the old data 1 was put in, or the dirty data problem we were talking about .

3.2.1 Solution

This problem is the concurrency of our writing and reading . We can still solve this problem by locking , but after locking, the system is a bit cumbersome .

So, we now consider another question, if our data is frequently modified, should we still put the cache?

If the data is frequently modified, in order to ensure the consistency of the data in our cache, a lock is added for writing and reading data, which will cause our entire system to be slow . It is better to check the database directly without locking.

Therefore, for data that is frequently modified, we still read the database directly .

Four, summary

Therefore, either of these two modes will work, but if you really want to solve the cache consistency problem in a precise manner, you can have the following solutions

No theory is a dual-write mode or failure mode, it can lead to inconsistencies cache . That is, if multiple instances are updated at the same time, something will happen. How to do?

1. If it is user latitude data (order data, user data), the probability of this concurrency is very small, do not consider this problem, cache data plus expiration time, trigger the active update of read every once in a while.

2. If it is basic data such as menus and product introductions, you can also use canal to subscribe to binlog .

.3. Cached data + expiration time is also sufficient to meet the requirements of most businesses for caching .

.4. Ensure concurrent reading and writing by locking, and queue up in order when writing. Reading does not matter. So it is suitable to use a read-write lock . (Business is not related to heart data, temporary dirty data is allowed to be ignored);

to sum up:

The data we can put in the cache should not have high real-time and consistency requirements. So add the expiration time when caching the data to ensure that you get the latest data every day .

We should not over-design and increase the complexity of the system

When encountering data with high real-time and consistency requirements, you should check the database, even if it is slower .

The consistent solution of our system:

1. All cached data has an expiration time, and the next query triggers an active update when the data expires

2. When reading and writing data, add a distributed read-write lock

Frequently written and frequently read data will definitely have a great impact on performance, but in the case of more reads and less writes, the impact is minimal, because for read-write locks, read-read conditions are equivalent to no locks. Moreover, a distributed read-write lock is required , because there are multiple instances to ensure that each server is locked .

Video tutorial

Guess you like

Origin blog.csdn.net/qq_38826019/article/details/115047374
Recommended