Redis的缓存击穿再理解

2021/07/23

前言

近期去了一家公司面试,面试体验相当的好,与两个大佬闲聊一样,聊得过程中真的是自愧不如抽丝剥茧般体会到自己的菜,不仅对知识点垂直度还是不够深,甚至让我感觉自己说的话都很业余,让我记忆深刻的是一个线程安全问题我解释半天,大佬一句竞态条件,当脑裂问题我解释半天,大佬一句不可达,真的是把我把握的稳稳的。其中有非常多有趣的问题,真的很值得反复的思考,其实我聊完心里很激动的,回去对一些问题复盘再思考。

问题

其中有个问题,真的让我回来仔细想过后脸红了,问题大概如下

说用Redis做缓存来提高吞吐量,先查Redis,Redis没有再查db。现在一个热门key过期了,现在100个线程过来,因为是同时过来所以大家都没命中Redis,它们会同时去查询数据库然后再写入Redis,怎么改进一下,没有标准答案,说什么都行

当时听到以后其实我仔细想了一下,可能是因为紧张没反应过来,当时听得非常有趣,回来一想这不就是缓存击穿吗,这真不是我吹,八股文缓存雪崩、击穿、穿透我两年前就会背,怎么得给我解释一边…我就反应不过来了…..,这就是只会背没有理解也没运用的尴尬….

说实话我自己是为自己觉得挺尴尬的,也挺后悔当时没回答的挺好的,因为没理解这个场景的意思。自己还是太菜,只会背八股文还是没用,虽说运用的话有点难度这种超高并发的场景确实在实际工作很难遇到,但是还是没把知识点结合场景去理解。

其实问题不难,也是很开放,回来自己想了一下,写了几种方法,也希望大家参考参考我经历,结合一下场景去学习。

缓存击穿应对方法

key永不过期过期时间

设置key永不过期,一劳永逸也是非常暴力的方法。

这里的过期除了不给TTL还有一种含义就是不断地给锁续期(假永久),更新key的时候把ttl到期时间戳随value一起写入,每次读取出来的时候判断比较时间戳和当前时间对比,快过期了就延长一下这个value内的时间戳。这样的好处就是,比如我设置还剩1天的时候就续期,如果这个key过期了说明在1天内其实都没有请求访问这个key,那这个key也就不是热点key也没必要担心它可能会对db的影响。也不会因为把它设置成永久后导致内存的堆积。

方法级别加锁

加锁就很好理解,在方法上加synchronized,使得所有的请求都串行化,只有第一个请求执行完才会放行第二个请求。如果key过期自然第一个请求就会重新查表回写到redis,第二个请求乃至后面所有的请求都会命中redis。

缺点显而易见,这把锁实在是太重了,串行化所有的请求,显著的降低了吞吐量,在高并发的线上环境影响是可感知的。

查db加锁串行化

在查询db的时候进行加锁,可以使用lock(在finally记得释放),因为细化锁的粒度,只有key失效的请求到去查询db的时候才会串行化,理论吞吐量和可用性平衡的比较好。

缺点是依然会做无用功,虽然所有的请求不会并发打到db,但是依然会one by one的去查询。

互斥锁只让一个请求去查db

可以从刚刚查询db加锁的方法上进行改进一下,在查db加锁串行化中当第一个请求过来它去查表的时候,其他请求是会被堵塞的,等锁释放了其他请求再竞争锁然后再放一个进去查询,直到所有请求都处理完。那既然有一个请求去查表了,其他请求都在堵塞状态那我们能不能让其他请求不堵塞等一个请求查表结束以后直接就从缓存里面拿数据。

基于这个想法,很容易想到用互斥锁,因为现在都是分布式下微服务所以下面写了一个用redis实现的伪码

// 查询redis
redis = getRedis();
// 判断缓存是否过期了
if result == nil {
  // 假设同时100个请求过来,用互斥锁先放一个进去查db,这里使用setex防止死锁
  if redis.setex {
    // 查db 放缓存
    result = getDB();
    redis.set(result)
  }
  // 后面没拿到锁的都在这里cas(这里也可以设置自旋次数,防止异常情况死锁)
 while(result == nill) {
  // 查redis
  result = redis.getRedis();
 }
}
// 如果上面设置了自旋次数下面可根据业务重要性在判断若result还是为null是继续查表还是直接返回fail

后言

本身这个问题也是开放的,因为这种场景下比较难遇到一些,原来看到缓存击穿都是说会直接把mysql打崩,但其实可能只是无意义的并发查询而已,或许会影响接口性能,做相应的处理可以使得这种请求更加“优雅”。

(转载本站文章请注明作者和出处 没有气的汽水



┌┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┐
├ 文章已经完啦, 想要第一时间收到文章更新可以关注↓ ┤
└┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┘

Post Directory






下面是评论区,欢迎大家留言探讨或者指出错误哈