缓存空对象Service public class UserService { Autowired private RedisTemplateString, Object redisTemplate; Autowired private UserMapper userMapper; public User getUserById(Long id) { String key user: id; // 查询缓存 User user (User) redisTemplate.opsForValue().get(key); if (user ! null) { return user.getId() null ? null : user; // 返回空对象表示不存在 } // 查询数据库 user userMapper.selectById(id); if (user null) { // 缓存空对象设置短过期时间 redisTemplate.opsForValue().set(key, new User(), 5, TimeUnit.MINUTES); return null; } redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES); return user; } }2. 布隆过滤器将所有合法key存入Bloom Filter请求先过滤器不存在的key直接拖到。回到顶部二、缓存击穿问题描述查询一个缓存刚好失效的key大量请求同时打到数据库。解决方案互斥锁推荐public User getUserById(Long id) { String key user: id; User user (User) redisTemplate.opsForValue().get(key); if (user ! null) return user; // 加分布式锁 String lockKey lock:user: id; Boolean locked redisTemplate.opsForValue() .setIfAbsent(lockKey, 1, 10, TimeUnit.SECONDS); if (Boolean.TRUE.equals(locked)) { try { // 再次查询缓存可能其他线程已经写入 user (User) redisTemplate.opsForValue().get(key); if (user null) { user userMapper.selectById(id); redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES); } } finally { redisTemplate.delete(lockKey); } } else { // 未获得锁稍等重试 Thread.sleep(50); return getUserById(id); } return user; }回到顶部三、缓存雪崩问题描述大量缓存key同时失效大量请求同时打到数据库。解决方案1. 过期时间加随机偶数// 设置缓存时加随机偶数避免同时失效 int randomExpire 30 new Random().nextInt(10); // 30~40分钟 redisTemplate.opsForValue().set(key, user, randomExpire, TimeUnit.MINUTES);2. 多级缓存本地缓存Caffeine Redis两级即使 Redis雪崩也有本地缓存托底。3. 缓存预热项目启动时提前将热点数据加载到缓存。回到顶部总结问题原因解决方案缓存穿透key不存在空对象 / 布隆过滤器缓存击穿key失效瞬间互斥锁 / 逆机制缓存雪崩大量同时失效随机过期 / 多级缓存
查询一个数据库和缓存中都不存在的key,每次请求都打到数据库,大量请求可能拖垃数据库。
发布时间:2026/6/29 22:45:14
缓存空对象Service public class UserService { Autowired private RedisTemplateString, Object redisTemplate; Autowired private UserMapper userMapper; public User getUserById(Long id) { String key user: id; // 查询缓存 User user (User) redisTemplate.opsForValue().get(key); if (user ! null) { return user.getId() null ? null : user; // 返回空对象表示不存在 } // 查询数据库 user userMapper.selectById(id); if (user null) { // 缓存空对象设置短过期时间 redisTemplate.opsForValue().set(key, new User(), 5, TimeUnit.MINUTES); return null; } redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES); return user; } }2. 布隆过滤器将所有合法key存入Bloom Filter请求先过滤器不存在的key直接拖到。回到顶部二、缓存击穿问题描述查询一个缓存刚好失效的key大量请求同时打到数据库。解决方案互斥锁推荐public User getUserById(Long id) { String key user: id; User user (User) redisTemplate.opsForValue().get(key); if (user ! null) return user; // 加分布式锁 String lockKey lock:user: id; Boolean locked redisTemplate.opsForValue() .setIfAbsent(lockKey, 1, 10, TimeUnit.SECONDS); if (Boolean.TRUE.equals(locked)) { try { // 再次查询缓存可能其他线程已经写入 user (User) redisTemplate.opsForValue().get(key); if (user null) { user userMapper.selectById(id); redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES); } } finally { redisTemplate.delete(lockKey); } } else { // 未获得锁稍等重试 Thread.sleep(50); return getUserById(id); } return user; }回到顶部三、缓存雪崩问题描述大量缓存key同时失效大量请求同时打到数据库。解决方案1. 过期时间加随机偶数// 设置缓存时加随机偶数避免同时失效 int randomExpire 30 new Random().nextInt(10); // 30~40分钟 redisTemplate.opsForValue().set(key, user, randomExpire, TimeUnit.MINUTES);2. 多级缓存本地缓存Caffeine Redis两级即使 Redis雪崩也有本地缓存托底。3. 缓存预热项目启动时提前将热点数据加载到缓存。回到顶部总结问题原因解决方案缓存穿透key不存在空对象 / 布隆过滤器缓存击穿key失效瞬间互斥锁 / 逆机制缓存雪崩大量同时失效随机过期 / 多级缓存