Data is stored in the database. In order to speed up business access, we put some data in the database in the cache. Then the question is, how to ensure the consistency of the data in the db and the cache? We have listed 5 methods, everyone understands them, and then chooses them according to the business.
Plan 1
Get the cache logic
Use the timer to refresh the cache in redis regularly .
db update data logic
Update the data without considering the data in the cache, just update the data directly
Existing problems
The consistency of the data in the cache and the data in the db may not be so timely, but in the end, the data is consistent at a certain point in time.
Plan 2
Get the cache logic
c1: Get the corresponding in redis according to the key The value
c2: If the value exists, return the value directly; if the value does not exist, continue with the following steps
c3: Get the value from the database, assign it to the value, and then set the key-> Put value into redis and return value
update db logic
u1: start db transaction
u2: update data
u3: Submit db transaction
u4: Delete the cache of current data in redis
Existing problems
- The success of u3 above and failure of u4 will cause the cache deletion to fail, resulting in inconsistencies between the data in the cache and the db data.
- If many threads arrive at c2 at the same time and find that the cache does not exist, and request c3 to access db at the same time, it will put a lot of pressure on db
Solution 3
Get Cache Logic
c1: get the corresponding value in redis according to the key
c2: if the value If it exists, return the value directly; if the value does not exist, continue with the following steps
c3: Get the value from the database, assign it to the value, then put the key->value into redis, and return the value
update db logic
u1: delete the cache of current data in redis
u2: start db transaction
u3: Update data
u4: Submit db transaction
Existing problems
- The thread that updates the data executes u1 successfully After that, when u2 is not yet executed, the thread that gets the cache just executes the logic from c1 to c3. At this time, the old data will be put into redis, resulting in inconsistent redis and db data.
- There are also solutions The problem mentioned in 2: If many threads arrive at c2 at the same time and find that the cache does not exist, and request c3 to access db at the same time, it will cause a lot of pressure on db
Solution 4
Improve solution 2 to ensure that the delete cache operation will be executed after the db update is successful. We can achieve this through reliable messages. Reliable messages can ensure that the update db operation and the deletion of the cache in redis will eventually be either All success or failure depends on eventual consistency to achieve.
The process after improvement is as follows.
Get cache logic
c1: get the corresponding value in redis according to the key
c2: if the value exists, Return value directly; if value does not exist, continue with the following steps
c3: Get the value from the database, assign it to value, then put key->value into redis, and return value
Update db logic
u1: start db transaction
u2: update data
u3: post a message to delete redis cache< /p>
u4: Submit db transaction
Message consumer-consumer who clears redis cache
Accept After clearing the messages cached in redis, clear the corresponding cache in redis.
Existing problems
- There is a certain time delay between updating db and clearing the cache in redis. During this time, The data cached by redis is old, which means that the db and cached data are inconsistent during this period, but they will eventually be the same. The inconsistency time may be relatively small (this depends on the efficiency of message consumption)
< li>There is also the same problem mentioned in Scheme 2: If many threads arrive at c2 at the same time and find that the cache does not exist, and request c3 to access db at the same time, it will cause great pressure on db
For reliable messages, you can see
- Talk about mq usage scenarios
- Talk about how to deliver messages to mq in the business system Ways
- Talk about several ways of mq message consumption
- How to ensure that messages are consumed at least once?
Method 5
Let’s first understand some knowledge.
Several methods in redis
get(key)
Get the key Value, if it exists, then return; if it does not exist, return nil
setnx(key,value)
The meaning of setnx It is SET if Not Exists. This method is atomic. If the key does not exist, the current key is set successfully and returns 1; if the current key already exists, the current key fails to be set and returns 0
del(key)
Delete the value corresponding to the key from redis
database-related knowledge
select v from t where t.key = #key# for update;
update t set v = #v# where t.key = #key#;
The above two sql will block each other, until one of them is submitted, the other can continue to execute.
Next, we will use the above knowledge to achieve strong consistency between DB and cache.
Update data logic
1. Open db transaction 2.update t set v = #v# where t.key = # key#;3. Delete the cache in redis according to the key: RedisUti.del(key); 4. Submit the db transaction
Get the cache logic
/*Public account: Passerby Java* The former Ali P7 who has worked for 10 years shares the technical dry goods of Java, algorithms, and databases! * Reliable technology changes fate and allows family members to live a more decent life. */public class CacheUtil {//Acquire the corresponding value in the cache according to the key public static String getCache(String key) throws InterruptedException {String value = RedisUtils.get(key); if (value != null) {return value;} / /Expiration time is the current time + 5 seconds String expireTimeKey = key + "ExpireTime"; long expireTimeValue = System.currentTimeMillis() + 5000; //setnx is an atomic operation, so only one will succeed int setnx = RedisUtils.setnx(expireTimeKey, expireTimeValue + ""); if (setnx == 0) {expireTimeValue = Long.valueOf(RedisUtils.get(expireTimeKey)); //If expireTimeValue is less than the current time, it means that the expireTimeKey has expired, delete it if (System.currentTimeMillis( )> expireTimeValue) {//Delete the expireTimeKey corresponding to RedisUtils.del(expireTimeKey);} else {//Sleep for 1 second to continue to get TimeUnit.SECONDS.sleep(1);} //Retry return getCache(key);} else {//1. Open db transaction start transaction; //2. Execute update t set v = #v# where t.key = #key# for update; Assign the value of v to value update t set v = #v# where t.key = #key# for update; RedisUtils.set(key, value); //3. Commit db transaction commit transaction;} return value;} //redis tool class, internal method is pseudo code public static class RedisUtils {//Get value according to key public static String get (String key) {return null;} //Set the value corresponding to the key public static void set(String key, String value) {} //Delete the value corresponding to a key in redis public static void del(String key) {} / The meaning of /setnx is SET if Not Exists. This method is atomic. If the key does not exist, //Set the current key successfully and return 1; if the current key already exists, set the current key failed and return 0 public static int setnx (String key, String value) {return 1;} }}
This method can ensure that the caches in db and redis are strongly consistent at the same time.
expireTimeKey in order to prevent some thread from executing
RedisUtils.setnx(expireTimeKey, expireTimeValue + "");
returns 1, indicating that setnx succeeded, and the system executes the next line of code After hanging up, it will cause the db data to fail to load into redis. The code:if (System.currentTimeMillis()> expireTimeValue)
is to give other threads the opportunity to get the expiration time, and directly after the expiration is found Delete, so that other threads have the opportunity to load db data into redis.
The former Ali P7 who has worked for 10 years shares the technical dry goods of Java, algorithms, and databases! Reliance on technology changes fate and allows family members to live a more decent life! If you like, please pay attention to the official account: Passerby Java