高并发-缓存和缓存失效

版权声明:小雷FansUnion的版权声明 https://blog.csdn.net/FansUnion/article/details/82594746

最近需要对别的部门提供一个接口,第1版直接查库,第2版增加了Redis缓存。

//具体执行查询
    private List<RecommendOuterVo> doQueryRecommendOuterVoList(ServiceAdCondition serviceAdCondition) {
        //根据参数构造缓存key
        String cacheKey = KeyPerBuilderUtil.buildRecommendOutListKey(serviceAdCondition);
        //redis
List<RecommendOuterVo> recommendOuterVoList = doQueryRecommendOuterVoListFromRedis(serviceAdCondition,cacheKey);
        //redis没有,再从数据库查1次
        if(CollectionUtils.isEmpty(recommendOuterVoList)){
            recommendOuterVoList=doQueryRecommendOuterVoListFromDb(serviceAdCondition);
       
            //db查询之后,更新到redis,5分钟
            if(CollectionUtils.isNotEmpty(recommendOuterVoList)) {
                Integer cacheSeconds = 5 * 60;
                String cacheValue = JSON.toJSONString(recommendOuterVoList);
                redisUtil.setex(cacheKey, cacheSeconds,cacheValue);
            }
        }else{
            logger.info("RecommendServiceOuterProviderImpl->doQueryRecommendOuterVoListFromRedis,voList size is {}",recommendOuterVoList.size());
        }
        return recommendOuterVoList;
    }

写好之后,同事A过问了下缓存设计,在考虑要不要 特别注意“高并发下的缓存失效、缓存穿透、瞬间大量查询DB”。

最终没有去在代码实现,参考了几篇文章,做了一点总结,然后和同事B探讨了下。

参考过的文章

缓存穿透、缓存并发、缓存失效之思路变迁

https://www.jianshu.com/p/d96906140199

最佳实践 缓存穿透,瞬间并发,缓存雪崩的解决方法

https://blog.csdn.net/fei33423/article/details/79027790

我的总结,仅供参考,不做过多解释了

业务场景:
  1、1个key
  String CACHE_KEY = "cache_key";
  首页-xxx:单个key的解决方案
  托底Redis1个月、JVM缓存3分钟、正常Redis5分钟、DB数据库、配置中心)

  2、多个key。
  根据多个参数,动态构造( platform,sortId)
  
  
  业务方1:缓存就1个key。每分钟1000到3000。
  业务方2:platform+sortId(30个左右),大约30个key。
  业务方3:待接入
  
  1个调用方,1秒,1000到3000。
  2个调用方,2000到6000。
  
  
  1、多个key同时缓存失效
     比如  首页4个xx栏目,4个独立的key,就有可能同时失效。
     托底不常用,JVM和Redis可能同时失效。DB承压。DB异常,查询配置中心。DB完全挂了,配置中心或托底缓存。
     
  2、同1个key多台机器同时缓存失效,导致高并发穿透DB(多个的话,翻几倍)
  1级缓存:同1个key,用redis(中心化)同时失效。
  
  
  解决办法:
  JVM本地缓存(3分钟)+Redis缓存(5分钟)。
  
  4台机器(A、B、C、D)可能会在JVM缓存同时失效。
  不会同时在2级缓存同时失效?仍然可能。
  
  
  10点1分,JVM缓存(10点4分过期),Redis(10点6分)。
  10点4分,JVM缓存(10点7分过期),Redis(10点6分+5,11分过期)
  10点7分,JVM缓存(10点10分过期),Redis(11过期)
  
  2次缓存过期时间, 距离时间 从2变为了1。
    
    
  10点10分,JVM缓存(10点13分过期),Redis(16过期)
  10点13,
  10点16
  2次缓存过期时间, 距离时间 从1变为了0。
  

这充分,2级缓存也无法避免“缓存穿透,大量查询直接到DB”。


  
  说明1:
  JVM固定3分钟,Redis不能固定为6分钟。
  答1:存在2个都为null的情况。
  3分钟和5分钟不会存在这种情况。

  (仔细分析之后,发现不对)
  
  说明2:
  2级缓存之后,多个key还有必要分开 过期吗?
  比如:JVM本地缓存(1到5分钟)+Redis缓存(2级缓存,5到10分钟)。
   
   
  答2:多个key缓存同时失效
  过期时间,分散化。1到10分钟随机,避免多个key-value同时过期。
  参考:https://www.jianshu.com/p/d96906140199    
  (2级缓存之后,多个key同时失效,只会发生在同1级缓存中)

  2级缓存,时间随机,可以减少“同时失效”的情况,但无法避免。


  说明3:
  早上9点,瞬间,从0到3000个访问
  答3:正常情况,不太可能。
  (秒杀场景,压测场景) 分布式锁,只让1台机器查询db

  说明4.定时任务(不太行,适合同1个key)
  platform+sortId,定时任务不断查询,1分钟主动更新缓存。
  (很多key不查询,浪费很多空间。key太多,查询数据库很频繁)
  
  说明5:托底缓存、配置中心,不现实。多个key,而且key是动态增加和减少的。
  首页xxx,1个key,而且是一直使用。

如果要实现,目前想到的解决方案:

JVM缓存(1到5分钟)+Redis缓存(6到30分钟)。

不同key,配置不同的过期时间。("123,456" 1分钟过期,"789,234" 1.8分钟过期)

如果查询Redis为空,通过分布式锁,只让1台机器去查询DB,其它线程等待50毫秒。

if(jvm缓存有值){

   return cacheJvm;

if(redis缓存有值){

  return cacheRedis;

}else{

   lock = get lock();

  if(lock){

     dbData=queryDb();

     updateJvmCache(buildKey(condition),dbData, random time(1-5);

     updateRedis(buildKey(condition),dbData, random time(6,10);

  } 

}

分布式锁

  1、直接redis

   while(set失败){

              sleep100毫秒。

            查询redis。

          有值就跳出,返回。

 } 
 2、Zookeeper

分布式锁,参考以前转载的文章。

一般公司的一般场景,好像没有必要考虑 缓存穿透的问题。

猜你喜欢

转载自blog.csdn.net/FansUnion/article/details/82594746