缓存击穿雪崩详解

5.1 缓存击穿

 

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于 并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力 瞬间增大,造成过大压力。

有些数据是错误数据没有必要查库的数据,例如,有些瞎搞的人输入错误的订单号,这种情况下我们可以采用布隆过滤来做验证,防止击穿,去查库。

guava工具包中提供了布隆过滤的方法。

布隆过滤技术主要是利用hash去做映射,具体的细节,就不在这细说了。

接下来看一下简单的代码应用:

 @PostConstruct //对象创建后,自动调用本方法    public void init(){//在bean初始化完成后,实例化bloomFilter,并加载数据        List<Provinces> provinces = this.list();        //当成一个SET----- 占内存,比hashset占得小很多        bf = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), provinces.size());// 32个        for (Provinces p : provinces) {            bf.put(p.getProvinceid());        }    }    @Cacheable(value = "province")    public Provinces detail(String provinceid) {        //先判断布隆过滤器中是否存在该值,值存在才允许访问缓存和数据库        if(!bf.mightContain(provinceid)){            System.out.println("非法访问--------"+System.currentTimeMillis());            return null;        }        System.out.println("数据库中得到数据--------"+System.currentTimeMillis());        Provinces provinces = super.detail(provinceid);        return provinces;    }

 

 

 

5.2 缓存雪崩

 

缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压 力过大甚至 down 机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩 是不同数据都过期了,很多数据都查不到从而查数据库。

可以简单的理解为:大量的击穿造成雪崩。

 

 

解决方案:

 

对操作数据库枷锁,只让一个线程去查库,查到数据后返回更新到缓存redis中,然后其他人就可以从缓存中获取,无需再去查库。

 

此时需要对数据库的查询操作,加锁 ---- lock (因考虑到是对同一个参数数值上 一把锁,此处 synchronized 机制无法使用) 加锁的标准流程代码如下(一样解决击穿的问题):

 

/** * 缓存雪崩 *///@Service("provincesService")public class ProvincesServiceImpl3 extends ProvincesServiceImpl implements ProvincesService{    private static final Logger logger = LoggerFactory.getLogger(ProvincesServiceImpl3.class);    @Resource    private CacheManager cm;    private ConcurrentHashMap<String, Lock> locks = new ConcurrentHashMap<>();//线程安全的    private static final String CACHE_NAME = "province";    public Provinces detail(String provinceid) {        // 1.从缓存中取数据        Cache.ValueWrapper valueWrapper = cm.getCache(CACHE_NAME).get(provinceid);        if (valueWrapper != null) {            logger.info("缓存中得到数据");            return (Provinces) (valueWrapper.get());        }        //2.加锁排队,阻塞式锁---100个线程走到这里---同一个sql的取同一把锁        doLock(provinceid);//32个省,最多只有32把锁,1000个线程        try{//第二个线程进来了            // 一次只有一个线程             //双重校验,不加也没关系,无非是多刷几次库            valueWrapper = cm.getCache(CACHE_NAME).get(provinceid);//第二个线程,能从缓存里拿到值?            if (valueWrapper != null) {                logger.info("缓存中得到数据");                return (Provinces) (valueWrapper.get());//第二个线程,这里返回            }            Provinces provinces = super.detail(provinceid);            // 3.从数据库查询的结果不为空,则把数据放入缓存中,方便下次查询            if (null != provinces){                cm.getCache(CACHE_NAME).put(provinceid, provinces);            }            return provinces;        }catch(Exception e){            return null;        }finally{            //4.解锁            releaseLock(provinceid);        }    }    private void releaseLock(String userCode) {        ReentrantLock oldLock = (ReentrantLock) locks.get(userCode);        if(oldLock !=null && oldLock.isHeldByCurrentThread()){            oldLock.unlock();        }    }    private void doLock(String lockcode) {//给一个搜索条件,对应一个锁        //provinceid有不同的值,参数多样化        //provinceid相同的,加一个锁,---- 不是同一个key,不能用同一个锁        ReentrantLock newLock = new ReentrantLock();//创建一个锁        Lock oldLock = locks.putIfAbsent(lockcode, newLock);//若已存在,则newLock直接丢弃        if(oldLock == null){            newLock.lock();        }else{            oldLock.lock();        }    }}

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值