Detailed explanation of Redis cache penetration, breakdown, and avalanche interview questions

cache penetration

question:

It means that the data requested by the client cannot be found in the cache, nor is it stored in the database, and the client continues to initiate requests. In this way, it cannot be queried in the database every time, and there will never be this data in the cache.

In this case, the client keeps accessing, which will put a lot of pressure on the back-end database.

solution:

Solution 1: Store null values ​​in redis

After querying the database, store the user in redis, and store a null value in the value, so that the next time the user comes to visit, it can be returned directly from redis. But in order to prevent these meaningless data from occupying memory, set the effective time to be short.

  • Advantages: simple implementation, easy maintenance
  • shortcoming:
    • Will take up more memory consumption
    • cause short-term inconsistencies
if ("数据" == null) { 
    // 如果数据库中不存在 返回错误信息 存储到redis中 value设置为null,有效时间设置短点 防止出现缓存穿透两分钟 
    String key = "用户id"; 
    stringRedisTemplate.opsForValue().set(key,"",2L,TimeUnit.MINUTES); 
}
// 判断缓存是否为"" 直接返回 
if ("".equals("数据")) {
 return "数据错误"; 
}

Option 2: Use a Bloom filter

Every time the client sends a request, it first goes to the Bloom filter to check whether there is such data, if it returns directly, if not, it goes to redis to look it up. And so on.

This Bloom filter copies data similar to bytes in mysql, so there may be misjudgment of filtering, resulting in cache penetration

  • Advantages: less memory usage, no redundant keys
  • shortcoming:
    • more complicated to implement
    • There is a misjudgment operation

Other options:

  • Enhance the complexity of id to avoid guessing id rules
  • Do a good job of data basic format verification
  • Strengthen user permission verification

 

cache avalanche

question:

It refers to the failure of a large number of caches in a certain period of time or the downtime of redis services, so that a large number of requests to access the database bring huge pressure.

Cache collective invalidation:

The cache server is down:

This situation is more serious

solution:

Add a random value to the expiration time of each cache, so that the repetition rate of the cache expiration time will be reduced, and it is difficult to cause the collective cache failure event again. [Cannot solve cache server downtime]

Other options:

  • Build a redis cluster to improve service availability
    • After one machine goes down, other machines continue to provide services
  • Add a degraded current limiting policy to the cache business
    • Limit the number of concurrent requests
  • Add multi-level cache to business
    • Add multiple caches to reduce the frequency of accessing the database

cache breakdown

question:

Cache breakdown can also be understood as a hot key problem, that is, a key that is highly concurrently accessed and the cache reconstruction business is relatively complicated suddenly fails (there is no data in the database in the cache). At this time, there will be countless requests to access the database, causing The database is under enormous pressure.

solution:

Solution 1: Mutual exclusion lock [locking mechanism]

When multiple threads access concurrently, the lock obtained first will query the database first, and other threads need to wait [timed initiation of retry], in order to ensure that the release of the lock fails for some reason in the end, so when rebuilding the snatching lock, give the lock Set a validity period and make a bottom-up plan.

 

  • advantage:
    • no additional memory consumption
    • Guaranteed data consistency
    • simple to implement
  • shortcoming:
    • Threads that have not grabbed the lock need to wait, and performance is affected
    • possible deadlock

Lock logic:

// 获取锁
private boolean tryLock(String key){
    Boolean isLock = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10L, TimeUnit.SECONDS); 
    return BooleanUtil.isTrue(isLock); 
// 释放锁 
private void unLock(String key){
    stringRedisTemplate.delete(key); 
}

 

 Business logic:

// 尝试获取到互斥锁 
String lockKey = "lock:"+id; boolean tryLock = tryLock(lockKey);
// 判断是否获取到锁
try { 
    if (!tryLock) {
        // 没有拿到锁 
        Thread.sleep(50); 
        return queryShopWithBreakdown(id); 
    } 
    // 拿到锁 
    // 再次判断缓存中是否有数据,防止别的线程中途重建 
    String key = stringRedisTemplate.opsForValue().get(key); 
    if (StrUtil.isNotBlank(key)){
        // 如果有数据直接返回 不需要重建 
        return JSONUtil.toBean(key,User.class); 
    } 
    // 不存在查询数据库 
    user = getById(id); 
    // 防止后面线程抢先 
    Thread.sleep(500); 
    if (user == null) {
         return null; 
    } 
    // 将返回结果存入redis中 设置有效期30分钟
    stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(user),30L,TimeUnit.MINUTES); 
} catch (InterruptedException e) { 
    throw new RuntimeException(e); 
}finally { 
    // 释放锁,中途发生异常也需要释放 
    unLock(lockKey);
} 
// 返回数据 
return user;

 

Solution 2: Logic failure [hot data]

The logical expiration time is used instead of TTL, which is the sum of the current time and the expiration time. If the current time is greater than the expiration time, it proves that the data has expired and the cache needs to be updated. It is also necessary to snatch the mutex, but if it cannot be snatched, it will directly return the previous data without waiting. The snatched start of a new thread is responsible for updating the cache, resetting the expiration time, and finally releasing the lock.

  • Advantages: threads do not need to wait, better performance
  • shortcoming:
    • The data cannot be guaranteed to be consistent for a short time
    • has additional memory consumption
    • more complicated to implement

 Rebuild method:

public void saveRedis(Long id,Long expire){ 
    // 从数据库查询 
    User user = getById(id); 
    RedisData redisData = new RedisData(); 
    redisData.setData(user); 
    // 当前时间和过期时间相加
    redisData.setExpireTime(LocalDateTime.now().plusSeconds(expire)); 
    // 添加到缓存
    stringRedisTemplate.opsForValue().set(key+id,JSONUtil.toJsonStr(redisData)); 
}

 Business logic:

// 判断缓存是否过期 
RedisData redisdata = JSONUtil.toBean(userJson, RedisData.class);
JSONObject data = (JSONObject)redisdata.getData(); 
user = JSONUtil.toBean(data, User.class); 
LocalDateTime expireTime = redisdata.getExpireTime(); 
// 过期时间是否在当前时间后面 
if (expireTime.isAfter(LocalDateTime.now())){ 
    // 没过期 
    return shop; 
} 
// 过期 
// 尝试获取锁 
String lockKey = "lock:"+id; 
boolean isLock = tryLock(lockKey); 
if (!isLock){ 
    // 没有拿到锁 直接将之前过期数据返回 
    return shop; 
} 
try { 
    // 拿到锁 开启一个线程 
    new Thread(new Runnable() {
        @Override 
        public void run() { 
            // 重建缓存 
            saveShop2Redis(id,20L); 
        } 
    }).start(); 
} catch (Exception e) {
    throw new RuntimeException(e); 
}finally { 
    unLock(lockKey); 
}
// 返回数据 
return shop;

 

 

Guess you like

Origin blog.csdn.net/weixin_45934981/article/details/130198076