在java开发过程中redis几乎是我们项目开发的标配,所以我们会经常涉及到利用redis进行开发。那么在redis开发过程中,我们经常会遇到缓存穿透的问题。这篇文章我们就来给大家介绍下缓存穿透的解决方案。
首先说说什么是缓存穿透
缓存穿透指的是前端查询数据的时候,无法从缓存中查询到数据,从而导致这个查询每一次都要访问数据库。
那么缓存穿透的场景有哪些
查询一个不存在的数据:可能会发送一些无效的查询来触发缓存穿透。 查询一些非常热门的数据:如果一个数据被访问的非常频繁,那么可能会导致缓存系统无法处理这些请求,从而造成缓存穿透。 查询一些异常数据:这种情况通常发生在数据服务出现故障或异常时,从而造成缓存系统无法访问相关数据,从而导致缓存穿透。
基于缓存穿透的解决方案是什么
其实解决缓存穿透的方案主要是我们可以加一个布隆过滤器,使用布隆过滤器的步骤如下:
初始化一个布隆过滤器 当查询来的时候首先查询布隆过滤器是否有值,如果有值则直接返回为空(因为数据库查询不到数据才会设置这个值) 如果布隆过滤器没有值,则查询缓存 缓存查询不到,则去查询数据库 数据库查询到则返回查询结果,查询不到,则在布隆过滤器里面添加一个key值
伪代码
初始化一个布隆过滤器
package com.redis.demo.utils; import java.nio.charset.Charset; import com.google.common.hash.BloomFilter; import com.google.common.hash.Funnels; public class BloomFilterUtils { // 布隆过滤器的预计容量 private static final int expectedInsertions = 1000000; // 布隆过滤器误判率 private static final double mjRate = 0.001; private static BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), expectedInsertions, mjRate); /** * 向Bloom Filter中添加元素 */ public static void add(String key) { bloomFilter.put(key); } /** * 判断元素是否存在于Bloom Filter中 */ public static boolean mightContain(String key) { return bloomFilter.mightContain(key); } }
这里我们引入的maven依赖是
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.1-jre</version> </dependency>
业务的伪代码如下:
package com.redis.demo.controller; import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Jedis; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.RestController; import com.redis.demo.dao.UserDao; import com.redis.demo.model.User; import com.redis.demo.utils.BloomFilterUtils; import lombok.extern.slf4j.Slf4j; @RestController @Slf4j public class HelloController { @Autowired private RedisTemplate redisTemplate; @Autowired UserDao userDao; /** * 测试缓存穿透 */ private User getUserInfoById(Long userId) { // 先从布隆过滤器中判断此id是否存在 if (!BloomFilterUtils.mightContain(userId.toString())) { return null; } User user = null; // 查询缓存数据 String userKey = "user_" + userId.toString(); user = (User) redisTemplate.opsForValue().get(userKey); if (user == null) { // 查询数据库 user = userDao.getUserByUserId(userId); // 这里的话 if (user != null) { // 将查询到的数据加入缓存 redisTemplate.opsForValue().set(userKey, user, 300, TimeUnit.SECONDS); } else { // 查询结果为空,将请求记录下来,并在布隆过滤器中添加 BloomFilterUtils.add(userId.toString()); } } return user; } /** * 测试缓存击穿 */ private String getUserInfoById1() { return null; } /** * 测试缓存雪崩 */ private String getUserInfoById2() { return null; } }
以上就是完整的关于缓存穿透的解决方案。
备注:
1、对于缓存穿透这块的解决方案需要考虑实际的使用场景,如果对于数据同时用新增和查询,这块做缓存穿透的解决方案的时候就需要小心一点了。
最后按照惯例,附上本案例的源码,登录后即可下载
还没有评论,来说两句吧...