再谈缓存击穿问题

    在高并发场景中,为了减少数据库的压力,咱们会引入缓存。java

    一个简单的查询操做,以下所示:数据库

   

    伪代码以下:缓存

public Object getObjById(Integer objId){
      String key = "Lock:Obj:"+objId;
      //先从缓存中获取数据
      Obj obj= DistributeCacheUtil.get(key);
      if(obj!=null){
            //若是缓存中存在则直接返回缓存中数据
            return obj;
      }
      //若是缓存中不存在该数据,从数据库中查询
      obj= DbUtil.getObjById(objId);
      if(obj!=null){
         //若是 数据库中存在,将查询出来的数据放入缓存,设置过时时间
         DistributeCacheUtil.set("Lock:Obj:"+objId,obj,1000);
      }
      //返回
      return obj;
}

    但是这里出现一个问题,好比秒杀场景或者一些其余场景,某一时刻开始进行秒杀,这时候大量的请求会请求到后台,若是没有事先进行数据缓存预热,这个时候缓存里是没有数据的,那么大量的请求回去查询数据库,这样整个缓存就失去意义了。并发

    如何解决?分布式

    1.提早进行数据缓存预热,好比项目启动时,或者定时器,先将数据查询出来放到缓存。高并发

    2.加入锁,具体伪代码逻辑以下所示:线程

public Object getObjById(Integer objId){
      String key = "Lock:Obj:"+objId;
      //先从缓存中获取数据
      Obj obj= DistributeCacheUtil.get(key);
      if(obj!=null){
            //若是缓存中存在则直接返回缓存中数据
            return obj;
      }
      //若是缓存中不存在该数据
      //加锁,这里分为阻塞锁和非阻塞锁
      if(DistributeCacheUtil.tryLock(key)){
           //进锁之后先从缓存中获取数据
           //若是第一个线程持锁后,加数据加载进缓存后,后来进来的线程能够直接从缓存中拿
           obj= DistributeCacheUtil.get(key);
           if(obj!=null){
              //若是缓存中存在则直接返回缓存中数据
              return obj;
           }
           //若是缓存中仍是不存在,再从数据库中拿
           obj= DbUtil.getObjById(objId);
           if(obj!=null){
             //若是数据库中存在,将查询出来的数据放入缓存,设置过时时间
             DistributeCacheUtil.set("Lock:Obj:"+objId,obj,1000);
           }
           //返回
           return obj; 
      }else{
           //这里是处理分布式锁为非阻塞锁的状况,若是是阻塞式锁,这里就没必要要处理了
           //这里根据实际状况处理,好比直接返回null(秒杀场景,直接返回卖完了)
           //或者休眠1秒钟,再从缓存中拿
           //或者自旋去查询缓存
           return null;
      }
}
相关文章
相关标签/搜索