口试必问:Redis缓存穿透
口试必问:Redis缓存穿透 概念:缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,如许缓存永远不会生效,这些请求都会打到数据库。
[*]缓存空对象
[*]长处:实现简朴,维护方便
[*]缺点:
[*]额外的内存斲丧
[*]大概造成短期的不一致
[*]布隆过滤
[*]长处:内存占用较少,没有多余key
[*]缺点:
[*]实现复杂
[*]存在误判大概
缓存空对象代码演示:
public SysUserEntity queryById(Long id) {
String userStr = redisTemplate.opsForValue().get("user:id:" + id);
if (Objects.nonNull(userStr)) {
return BeanUtil.toBean(userStr, SysUserEntity.class);
}
SysUserEntity user = this.getById(id);
if (Objects.nonNull(user)) {
redisTemplate.opsForValue().set("user:id:" + user.getId(), JSONUtil.toJsonStr(user));
}
return user;
}
//假设: 用户每次入参都是id为-1,则每次都查不到缓存,都执行了 SysUserEntity user = this.getById(id) 这就是缓存穿透
public SysUserEntity queryById(Long id) {
String userStr = redisTemplate.opsForValue().get("user:id:" + id);
if (StrUtil.isNotBlank(userStr)) {
return BeanUtil.toBean(userStr, SysUserEntity.class);
}
SysUserEntity user = this.getById(id);
if (Objects.nonNull(user)) {
redisTemplate.opsForValue().set("user:id:" + id, JSONUtil.toJsonStr(user));
} else {
// 缓存空值,避免缓存穿透,存在一下2个缺点
//1.浪费内存,保存了无意义的值
//2.可能导致短期数据不一致。例如:线程A查询id=10,缓存空值;线程B插入id=10的数据但未更新缓存;线程C查询id=10,仍获取空值
redisTemplate.opsForValue().set("user:id:" + id, "", 30, TimeUnit.SECONDS);
}
return user;
}
布隆过滤器代码实现
@Component
public class BloomFilterInitializer {
@Resource
private SysUserDao userMapper;
@Getter
private RBloomFilter<Long> userBloomFilter;
@Resource
private RedissonClient redissonClient;
@PostConstruct
public void init() {
// 初始化布隆过滤器,预计插入 1000000 个元素,误判率为 0.01
userBloomFilter = redissonClient.getBloomFilter("userBloomFilter");
// 预计插入 1000000 个元素,误判率为 0.01
userBloomFilter.tryInit(1000000L, 0.01);
// 从数据库加载所有用户ID到布隆过滤器
List<SysUserEntity> sysUserEntities = userMapper.selectList(new LambdaQueryWrapper<>());
for (SysUserEntity user : sysUserEntities) {
userBloomFilter.add(user.getId());
}
}
}
public SysUserEntity queryById(Long id) {
// 布隆过滤器检查
RBloomFilter<Long> userBloomFilter = bloomFilterInitializer.getUserBloomFilter();
if (!userBloomFilter.contains(id)) {
return null; // 布隆过滤器判断ID不存在,直接返回空值
}
String userStr = redisTemplate.opsForValue().get("user:id:" + id);
if (StrUtil.isNotBlank(userStr)) {
if (userStr.isEmpty()) {
return null;
}
SysUserEntity user = JSONUtil.toBean(userStr, SysUserEntity.class);
return user;
}
SysUserEntity user = this.getById(id);
if (Objects.nonNull(user)) {
// 将查询结果存入缓存
redisTemplate.opsForValue().set("user:id:" + id, JSONUtil.toJsonStr(user), 30, TimeUnit.SECONDS);
} else {
// 缓存空值,避免缓存穿透
redisTemplate.opsForValue().set("user:id:" + id, "", 30, TimeUnit.SECONDS);
}
return user;
}
//注意:新增数据的时候也要加入到布隆过滤器
public SysUserEntity insert(SysUserEntity sysUserEntity) {
this.save(sysUserEntity);
redisTemplate.opsForValue().set("user:id:" + sysUserEntity.getId(), JSONUtil.toJsonStr(sysUserEntity));
bloomFilterInitializer.getUserBloomFilter().add(sysUserEntity.getId());
return sysUserEntity;
}
布隆扩展
如何选择符合的参数
预期插入元素数量(n)
[*]根据业务场景中必要存储的元素数量来设置。
[*]比方:
[*]假如你的体系中有 100 万个用户 ID,可以设置为 1000000L。
[*]假如用户数量大概会增长,可以适当增长该值(如 2000000L)。
误判率(p)
[*]根据业务对误判率的容忍度来设置。
[*]比方:
[*]假如业务可以接受 1% 的误判率,可以设置为 0.01。
[*]假如业务对误判率要求非常严格,可以设置为 0.001 或更低。
0误判如何如何实现:推荐利用HashSet,布隆过滤器无法实现0误判
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]