Redis process lock to avoid cache breakdown
First of all, due to the characteristics of the system, it knows which keys are hotkeys. For these hotkeys, which thread finds that it is empty now, it will be locked immediately to obtain the qualification for downward operation, and other blocks will be blocked (so that it will not put a lot of pressure on the db ( Otherwise, go to access db)) , and then immediately this UI thread requests db to get data to fill the cache
cache breakdown
For some keys with an expiration time set, if these keys may be accessed with high concurrency at certain points in time, it is a very "hot" data. At this time, there is a problem that needs to be considered: the problem of the cache being "broken down". The difference between this and the cache avalanche is that the cache is for a certain key, and the former is for many keys.
When the cache expires at a certain point in time, there are a large number of concurrent requests for this key at this point in time. These requests generally load data from the back-end DB and set it back to the cache when the cache expires. At this time, large concurrent requests It may instantly overwhelm the backend DB.
solution
1. Use a mutex key
The more common practice in the industry is to use mutex. To put it simply, when the cache is invalid (it is judged that the value taken out is empty), instead of going to load db immediately, use some operations of the cache tool with the return value of a successful operation (such as Redis's SETNX or Memcache). ADD) to set a mutex key, when the operation returns successfully, perform the load db operation and reset the cache; otherwise, retry the entire get cache method.
SETNX is the abbreviation of "SET if Not eXists", that is, it is only set when it does not exist, and it can be used to achieve the effect of locking. Before redis 2.6.1, the expiration time of setnx was not implemented, so here are two versions of the code for reference:
[java] view plain copy on CODE view code slice derived to my code slice
//Pre-2.6.1 stand-alone version lock
String get(String key) {
String value = redis.get(key);
if (value == null) {
if (redis.setnx(key_mutex, "1")) {
// 3 min timeout to avoid mutex holder crash
redis.expire(key_mutex, 3 * 60)
value = db.get(key);
redis.set(key, value);
redis.delete(key_mutex);
} else {
// Retry after other threads rest for 50ms
Thread.sleep(50);
get(key);
}
}
}
Latest version code:
[java] view plain copy on CODE view code slice derived to my code slice
public String get(key) {
String value = redis.get(key);
if (value == null) { //Represents that the cached value expires
//Set a timeout of 3min to prevent the failure of the del operation, the next cache expiration will not be able to load db
if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { // means the setting is successful
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key_mutex);
} else { //At this time, it means that other threads at the same time have loaded db and set it back to the cache. At this time, you can retry to get the cache value
sleep(50);
get(key); //retry
}
} else {
return value;
}