上一篇文章《Redis缓存穿透的解决方案》我们介绍了缓存穿透的解决方案,这篇文章我们介绍缓存击穿的解决方案。
什么是缓存击穿?
缓存击穿指的是在一些高并发访问下,一个热点数据从缓存中不存在,每次请求都要直接查询数据库,从而导致数据库压力过大,并且系统性能下降的现象。
其实缓存穿透和缓存击穿从业务上来看是一个意思,但是有一个前提,就是缓存穿透可以看作是平滑发生的,缓存击穿可以看做是瞬间高并发引起的。但是最终的效果缺不一样。
缓存击穿的场景有哪些?
缓存中不存在所需的热点数据:当系统中某个热点数据需要被频繁访问时,如果这个热点数据最开始没有被缓存,那么就会导致系统每次请求都需要直接查询数据库,造成数据库负担。 缓存的热点数据过期:当一个热点数据过期并需要重新缓存时,如果此时有大量请求,那么就会导致所有请求都要直接查询数据库。
缓存击穿的解决方案
在遇到缓存击穿问题时,我们可以在查询数据库之前,先判断一下缓存中是否已有数据 如果没有数据则使用Redis的单线程特性(分布式锁),先查询数据库然后将数据写入缓存中
缓存击穿的伪代码
/** * 测试缓存击穿 */ private User getUserInfoById1(Long userId) { // 先从缓存中获取值 String userKey = "user_"+userId.toString(); User user = (User) redisTemplate.opsForValue().get(userKey); if(user == null){ // 查询数据库之前加锁 String lockKey = "lock_user_"+userId.toString(); String lockValue = UUID.randomUUID().toString(); try{ Boolean lockResult = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 60, TimeUnit.SECONDS); if(lockResult != null && lockResult){ // 查询数据库 user = userDao.getUserByUserId(userId); if(user != null){ // 将查询到的数据加入缓存 redisTemplate.opsForValue().set(userKey, user, 300, TimeUnit.SECONDS); } } }finally{ // 释放锁 if(lockValue.equals(redisTemplate.opsForValue().get(lockKey))){ redisTemplate.delete(lockKey); } } } return user; }
以上就是缓存击穿的解决方案,这里和缓存穿透的差异性如下:
1、缓存穿透这块是数据在数据库没有查到的时候,把值放到布隆过滤器中避免重复查询空值。 2、缓存击穿这块主要是能在数据库查询到数据,为了避免瞬间的高并发,导致数据全部落到数据库去查询了,所以我们添加分布式锁,让数据直接去缓存中查询。
按照惯例,附上本案例的源码,登录后即可下载
还没有评论,来说两句吧...