本章来学习记录一下关于缓存三大典型问题其一的缓存穿透的基本解决方式:通过布隆过滤器以及设置null值。
为了实现这一demo,需要整合jedis和redisson,所以本文主要会从下面几个方面开始讲述:
- springboot整合Jedis
- springboot使用Redis的两种方式(Jedis和Redistemplate)
- springboot整合Redission布隆过滤器
- springboot使用布隆过滤器的四种方式(设置到redis内,手写,Redission,Guava)
- 编写代码测试缓存穿透
一、什么是缓存穿透
首先本文的目的就是学习怎么通过布隆过滤器解决缓存穿透,那么缓存穿透是什么呢,大致如下图。
为了解决数据库的压力问题,引入了缓存这一东西,将某些数据存入缓存后,接口直接从缓存取值,从而减轻数据库的压力。当客户端访问的数据在缓存中不存在时,就会到数据库查询,查到了再存入缓存,而缓存击穿就是恶意攻击,一直访问数据库中不存在的数据,导致直接穿过缓存,每次都击中数据库。
二、怎么解决缓存穿透
解决缓存穿透的方式可以通过布隆过滤网和空值设置法,本文会将两种方法都使用到。
1、布隆过滤器:可以理解为就是一个普通的过滤器拦截器,将数据通过add方法存入过滤器之后,通过它提供的contains方法判断是否存在某个值,返回值也是true或者false,详细的自己百度一下。他会有一定的误差(返回true,表示不一定存在;返回false,表示一定不存在),但是我认为解决缓存穿透可以不用考虑这个问题,因为只需要确认它一定不存在就可以了。具体使用时,在项目启动时可以查询数据库,将所有需要缓存的数据存入过滤器,每次接口调用时,通过它先判断一遍,存在后再走后面的逻辑(查缓存、查数据库)。
2、空值设置法:查询数据库时,若不存在该数据,设置一个过期时间短的缓存到redis,下次到缓存查询时就能查到该值,并直接返回对应的value值null,设置一个较短的过期时间是为了以防后面该值被误处理,一直查询处于空值,还有就是可能会出现大量的处于null值的缓存,占用缓存资源。
3、综上,本次demo的缓存穿透解决方式,如下(综合两种方式):
三、springboot怎么使用Redis
上面已经讲了如何解决缓存穿透,这小节就介绍一下,一般springboot怎么使用redis呢?很简单,通常有两种方式,一种是RedisTemplate,一种是Jedis。
- RedisTemplate:RedisTemplate是SpringDataRedis中对JedisApi的高度封装。
- Jedis:Jedis是Redis官方推荐的面向Java的操作Redis的客户端。
原生jedis效率优于redisTemplate。
前面有一章已经整合过 redisTemplate方式使用redis了,感兴趣的可以看看。【七】springboot整合redis(超详细)_
所以后面在这次demo中会使用jedis来操作redis。
四、springboot怎么使用布隆过滤器
springboot使用布隆过期器也有多种方式,比如使用谷歌的Guava。
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> </dependency>
或者使用redis提供的redisson。
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> </dependency>
本次demo将会集成redisson的方式使用布隆过滤器。
五、springboot整合Jedis
整合Jedis的方式很简单,直接引入依赖,配置好连接就可以了,如下:
1、引入依赖
2、新建配置文件(反正是学习,将配置写死在代码内也行)
3、编写redis配置文件(关于jedis的,后面会将redisson的也写在这个配置文件,我为了简便,通用配置信息)
通过ResourceBundle配合静态块,将配置信息读取到并设置到jedis连接池进行创建连接池。
PS:也可以不使用连接池,直接使用Jedis,但是那样的话,会每次连接都会创建新的Jedis对象,推荐使用连接池的方式,类似数据库的连接池。
然后通过 Jedis jedis = jedisPool.getResource();就可以得到Jedis对象,就可以通过Jedis提供的Api方法进行redis的操作了。
六、springboot整合Redisson
上面整合了Jedis,下面进行最后Redisson的整合,依旧是引入依赖,然后连接上缓存,再通过Redisson创建布隆过滤器即可。
1、引入依赖
2、修改redisConfig(上面创建的配置文件,新增redisson的连接,以及布隆过滤器的创建)
PS:文章最开始提过布隆过滤器是有误差率的,所有在创建时会有参数来控制这个误差率,
第一个参数代表大小,第二个参数代表容错率,具体的就不提了,百度学习吧。
再提一嘴,上述的bean要保证单例,要么自己实现或者交给spring管理就可以了,spring创建的bean默认是单例的。
六、准备demo所需
本小结,介绍一下本次demo最后需要的准备,本章的ORM框架使用的是Mybatis-plus,前面也有讲过具体的使用【四】springboot整合mybatis-plus(超详细)
所以需要创建mapper层等代码。
1、创建数据库表
CREATE TABLE `product` ( `pid` int NOT NULL AUTO_INCREMENT, `productName` varchar(255) COLLATE utf8_bin DEFAULT NULL, `price` varchar(255) COLLATE utf8_bin DEFAULT NULL, PRIMARY KEY (`pid`) ) ENGINE=InnoDB AUTO_INCREMENT=253 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin;
2、随便导入几百条数据
3、创建实体映射类
4、创建mapper层
5、创建项目初始化时将产品信息存入缓存和布隆过滤器的操作
关于实现的InitializingBean接口,在前面的章节也有说过,【问题篇】springboot项目通过数据库限制实例端口号_ 。
6、创建controller
具体的处理流程就是前面贴的流程图的处理方式,最后一小节进行测试。
七、测试
本次demo整合了swagger,所有直接通过swagger调用接口进行测试,前面有讲过如何整合swagger,【一】springboot整合swagger(超详细)_。
1、测试数据库、缓存 、布隆过滤器中都存在的数据。
布隆过滤器是二进制向量
可以看到走的是缓存。
2、测试数据库、缓存 、布隆过滤器中都不存在的数据。
控制台是空的,缓存和数据库都没走。
3、测试布隆过滤器中存在的数据,数据库、缓存不存在的数据(模拟原本存在的数据,被删除了)
模拟操作:启动服务后,将id为1的产品从数据库和缓存中删除。
会去缓存设置空值。
本次查询了数据库并缓存了一个null值。
当第二次请求时,查询id为1的产品时,如下:
会去查询缓存,不会再次查询数据库了。
其他的情况本章就不再测试了,感兴趣的兄弟可以自己试试,到此null值设置法和布隆过滤器的解决办法都测试出效果了。