在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、对于缓存穿透这块的解决方案需要考虑实际的使用场景,如果对于数据同时用新增和查询,这块做缓存穿透的解决方案的时候就需要小心一点了。
最后按照惯例,附上本案例的源码,登录后即可下载









还没有评论,来说两句吧...