缓存穿透,击穿,雪崩

缓存穿透

缓存穿透是指缓存和数据库中都没有数据,而用户不断发起请求这些请求会穿透直接访问数据库,如果发起id为-1或者id特别大的数据,就可以利用这个漏洞,对数据库造成压力。甚至压垮数据库
在这里插入图片描述
解决方案
设置缓存空对象:创建空对象并且将其缓存起来,同时设置一个过期时间(避免控制占用更多的储存空间),之后在访问这个数据得时候,则会从缓存中获取,这样保护了后端的数据源
代码示例
先从redis中查,查不到在从mysql查,mysql查不到,返回一个空对象存入redis并设置缓存时间,然后返回,以后这个请求来都访问redis中的空对象。

@Override
	public TbItem selectItemInfo(Long itemId) {
		//查询缓存
		TbItem tbItem = (TbItem) redisClient.get(ITEM_INFO + ":" + itemId + ":"+ BASE);
		if(tbItem!=null){
			return tbItem;
		}

		tbItem = tbItemMapper.selectByPrimaryKey(itemId);
		/********************解决缓存穿透************************/
		if(tbItem == null){
			//把空对象保存到缓存
			redisClient.set(ITEM_INFO + ":" + itemId + ":"+ BASE,new TbItem());
			//设置缓存的有效期
			redisClient.expire(ITEM_INFO + ":" + itemId + ":"+ BASE,30);
			return tbItem;
		}
		//把数据保存到缓存
		redisClient.set(ITEM_INFO + ":" + itemId + ":"+ BASE,tbItem);
		//设置缓存的有效期
		redisClient.expire(ITEM_INFO + ":" + itemId + ":"+ BASE,ITEM_INFO_EXPIRE);
		return tbItem;
	}

	/**
	 * 根据商品 ID 查询商品介绍
	 * @param itemId
	 * @return
	 */
	@Override
	public TbItemDesc selectItemDescByItemId(Long itemId) {
		//查询缓存
		TbItemDesc tbItemDesc = (TbItemDesc) redisClient.get(
            	ITEM_INFO + ":" + itemId + ":"+ DESC);
		if(tbItemDesc!=null){
			return tbItemDesc;
		}

		TbItemDescExample example = new TbItemDescExample();
		TbItemDescExample.Criteria criteria = example.createCriteria();
		criteria.andItemIdEqualTo(itemId);
		List<TbItemDesc> itemDescList =
				this.tbItemDescMapper.selectByExampleWithBLOBs(example);
		if(itemDescList!=null && itemDescList.size()>0){
			//把数据保存到缓存
			redisClient.set(ITEM_INFO + ":" + itemId + ":"+ DESC,itemDescList.get(0));
			//设置缓存的有效期
			redisClient.expire(ITEM_INFO + ":" + itemId + ":"+ DESC,ITEM_INFO_EXPIRE);
			return itemDescList.get(0);
		}
		/********************解决缓存穿透************************/
		//把空对象保存到缓存
		redisClient.set(ITEM_INFO + ":" + itemId + ":"+ DESC,new TbItemDesc());
		//设置缓存的有效期
		redisClient.expire(ITEM_INFO + ":" + itemId + ":"+ DESC,30);
		return null;
	}

缓存击穿

缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对一个key不停的进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,然后直接请求数据库了,有点像水滴石穿,主要特点就是大量请求并且持续,就像在一个屏障上开了一个洞。
在这里插入图片描述
解决方案
1.使用Redis分布式锁
2.设置热点数据在redis中永远不过期
在这里插入图片描述
基于redis分布式锁实现的思路是:当大量请求访问一个key时,使用该分布式锁让大量中请求中的一个请求获取该锁独占该key,然后这个请求执行业务,执行业务后释放锁,如果不释放会可能会由于业务处理失败,导致redis没有数据而死锁,导致其他的请求无限的等待。然后没有获得该锁的请求等待该业务处理后回调自身。
实例代码

 public TbItemDesc selectItemDescByItemId(Long itemId) {
        TbItemDesc tbItemDesc = (TbItemDesc) redisClient.get(ITEM_INFO+":"+itemId+":"+DESC);
        if(tbItemDesc != null){
            return tbItemDesc;
        }
        /******************解决缓存击穿问题*******************/
        if(redisClient.setnx(SETNX_LOCK_DESC+":"+itemId, itemId)) {
            try {
                tbItemDesc = tbItemDescMapper.selectByPrimaryKey(itemId);
                /*********解决缓存穿透问题*******/
                if (tbItemDesc == null) {
                    tbItemDesc = new TbItemDesc();
                    redisClient.set(ITEM_INFO + ":" + itemId + ":" + DESC, tbItemDesc);
                    redisClient.expire(ITEM_INFO + ":" + itemId + ":" + DESC, 30);
                    return tbItemDesc;
                }
                redisClient.set(ITEM_INFO + ":" + itemId + ":" + DESC, tbItemDesc);
                redisClient.expire(ITEM_INFO + ":" + itemId + ":" + DESC, ITEM_INFO_EXPIRE);
            }finally {
                //释放分布式锁
                redisClient.del(SETNX_LOCK_DESC+":"+itemId);
            }
            return tbItemDesc;
        }else{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return selectItemDescByItemId(itemId);
        }
    }

上述代码意思就是首先查询redis中的key值是否存在,如果不存在,在多个请求中,使用分布式锁锁到一个请求处理业务,然后释放锁,同时这个请求在处理业务的时候,也有其他请求进来,但是无法执行业务代码(因为当前锁被占有,进入else中睡眠一秒),等待被锁请求执行完业务,然后回调方法,此时业务执行完redis中有数据,直接返回数据**。只有是redis中该key不存在,才使用分布式锁,从mysql中查询出来然后缓存到redis返回**

缓存雪崩

缓存雪崩。指的是在某一时间端,缓存集中过期失效
在这里插入图片描述
解决方案:
缓存数据的过期时间设置随机或不同分类商品缓存不同周期或热门类目的商品缓存永不过期

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值