缓存三大问题:穿透、击穿、雪崩的解决方案
缓存穿透(Cache Penetration) 定义:查询不存在的数据,缓存和数据库都没有,每次请求都打到数据库 场景:恶意攻击,大量查询不存在的key 请求key="user:-1" → Redis没有 → DB没有 → 返回null 请求key="user:-2" → Redis没有 → DB没有 → 返回null ...大量请求直接打垮数据库 解决方案1:布隆过滤器 @Service public class UserService { @Autowired private RedissonClient redisson; @Autowired private UserMapper userMapper; private RBloomFilter<Long> userBloomFilter; @PostConstruct public void init() { userBloomFilter = redisson.getBloomFilter("user:bloom"); userBloomFilter.tryInit(10000000L, 0.01); // 加载所有userId List<Long> userIds = userMapper.selectAllUserIds(); userIds.forEach(userBloomFilter::add); } public User getUser(Long userId) { // 1. 布隆过滤器判断 if (!userBloomFilter.contains(userId)) { return null; // 一定不存在 } // 2. 查询Redis String key = "user:" + userId; User user = (User) redis.opsForValue().get(key); if (user != null) { return user; } // 3. 查询数据库 user = userMapper.selectById(userId); if (user != null) { redis.opsForValue().set(key, user, 3600, TimeUnit.SECONDS); } return user; } } 解决方案2:缓存空值 public User getUser(Long userId) { String key = "user:" + userId; // 1. 查询Redis User user = (User) redis.opsForValue().get(key); if (user != null) { if (user.getId() == null) { // 空对象标记 return null; } return user; } // 2. 查询数据库 user = userMapper.selectById(userId); if (user != null) { redis.opsForValue().set(key, user, 3600, TimeUnit.SECONDS); } else { // 缓存空对象,防止穿透 User emptyUser = new User(); redis.opsForValue().set(key, emptyUser, 60, TimeUnit.SECONDS); // 短期缓存 } return user; } 缓存击穿(Cache Breakdown) 定义:热点key过期瞬间,大量请求同时打到数据库 ...