善良几何 發表於 2026-3-24 14:45:00

Redis缓存实战:彻底解决缓存穿透、击穿、雪崩三大难题

<h2>前言</h2>
<p>Redis缓存是Java开发中最常用的技术之一,但缓存穿透、击穿、雪崩三大问题也是面试高频考题。本文结合实战代码,带你彻底搞懂这三大难题。</p>
<h2>一、缓存穿透</h2>
<h3>问题描述</h3>
<p>查询一个<strong>数据库和缓存中都不存在</strong>的key,每次请求都打到数据库,大量请求可能拖垃数据库。</p>
<h3>解决方案</h3>
<p><strong>1. 缓存空对象</strong></p>
<pre><code>@Service
public class UserService {
    @Autowired
    private RedisTemplate&lt;String, Object&gt; 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;
    }
}</code></pre>
<p><strong>2. 布隆过滤器</strong>:将所有合法key存入Bloom Filter,请求先过滤器,不存在的key直接拖到。</p>
<h2>二、缓存击穿</h2>
<h3>问题描述</h3>
<p>查询一个<strong>缓存刚好失效</strong>的key,大量请求同时打到数据库。</p>
<h3>解决方案</h3>
<p><strong>互斥锁(推荐)</strong></p>
<pre><code>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;
}</code></pre>
<h2>三、缓存雪崩</h2>
<h3>问题描述</h3>
<p>大量缓存key<strong>同时失效</strong>,大量请求同时打到数据库。</p>
<h3>解决方案</h3>
<p><strong>1. 过期时间加随机偶数</strong></p>
<pre><code>// 设置缓存时加随机偶数,避免同时失效
int randomExpire = 30 + new Random().nextInt(10); // 30~40分钟
redisTemplate.opsForValue().set(key, user, randomExpire, TimeUnit.MINUTES);</code></pre>
<p><strong>2. 多级缓存</strong>:本地缓存(Caffeine)+ Redis两级,即使 Redis雪崩也有本地缓存托底。</p>
<p><strong>3. 缓存预热</strong>:项目启动时提前将热点数据加载到缓存。</p>
<h2>总结</h2>
<table>
<tbody>
<tr><th>问题</th><th>原因</th><th>解决方案</th></tr>
<tr>
<td>缓存穿透</td>
<td>key不存在</td>
<td>空对象 / 布隆过滤器</td>
</tr>
<tr>
<td>缓存击穿</td>
<td>key失效瞬间</td>
<td>互斥锁 / 逆机制</td>
</tr>
<tr>
<td>缓存雪崩</td>
<td>大量同时失效</td>
<td>随机过期 / 多级缓存</td>
</tr>
</tbody>
</table>
<p><em>本文由AI辅助创作。</em></p>

</div>
<div id="MySignature" role="contentinfo">
   

---

📌 **如果觉得文章对你有帮助,欢迎点赞👍收藏⭐!**

💬 有问题或建议?欢迎在评论区留言讨论~

🔗 更多技术干货请关注作者:弥烟袅绕

📚 本文地址:https://www.cnblogs.com/czlws/p/19763978<br><br>
来源:https://www.cnblogs.com/czlws/p/19763978
頁: [1]
查看完整版本: Redis缓存实战:彻底解决缓存穿透、击穿、雪崩三大难题