Redis缓存三大问题 - 缓存击穿、雪崩篇

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。


上一篇文章中我们介绍了缓存穿透和应对方式,剩下的两个问题相对比较简单,本文我们再来看看如何解决。

缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据,由于出现大量的并发请求,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。

造成这种情况大致有两种情况:

  • 第一次查询数据时,没有进行缓存预热,数据并没有加入缓存当中。
  • 缓存由于到达过期时间导致失效。

解决思路:

  • 当缓存不命中时,在查询数据库前使用redis分布式锁,使用查询的key值作为锁条件;
  • 获取锁的线程在查询数据库前,再查询一次缓存。这样做是因为高并发请求获取锁的时候造成排队,但第一次进来的线程在查询完数据库后会写入缓存,之后再获得锁的线程直接查询缓存就可以获得数据;
  • 读取完数据后释放分布式锁。

代码思路:

public String queryData(String key) throws Exception {
    String data;
    data = queryDataFromRedis(key);// 查询缓存数据
    if (data == null) {
        if(redisLock.tryLock()){//获取分布式锁
            data = queryDataFromRedis(key); // 再次查询缓存
            if (data == null) {
                data = queryDataFromDB(key); // 查询数据库
                writeDataToRedis(data); // 将查询到的数据写入缓存
            }
            redisLock.unlock();//释放分布式锁
        }
    }
    return data;
}
复制代码

具体分布式锁的实现可以使用redis中强大的setnx命令:

/*
* 加锁
* key-键;value-值
* nxxx-nx(只在key不存在时才可以set)|xx(只在key存在的时候set)
* expx--ex代表秒,px代表毫秒;time-过期时间,单位是expx所代表的单位。
* */
jedis.set(key, value, nxxx, expx, time);
​
//解锁
jedis.del(key);
复制代码

通过在加锁的同时设置过期时间,还可以防止线程挂掉仍然占用锁的情况。

缓存雪崩

缓存雪崩是指缓存中数据大批量到过期时间,引发的大部分缓存突然同时不可用,而查询数据量巨大,引起数据库压力过大甚至宕机的情况。 需要注意缓存击穿和缓存雪崩的不同之处缓存击穿指的是大量的并发请求去查询同一条数据;而缓存雪崩是大量缓存同时过期,导致很多查询请求都查不到缓存数据从而查数据库。

解决方案:

  • 错开缓存的过期时间,可通过设置缓存数据的过期时间为默认值基础上加上一个随机值,防止同一时间大量数据过期现象发生。
  • 搭建高可用的redis集群,避免出现缓存服务器宕机引起的雪崩问题。
  • 参照hystrix,进行熔断降级。

总结:

随着Redis的使用日渐普及,越来越多的系统开始使用缓存技术,但伴随着便利的同时也因为使用不当造成了很多问题。只有在系统设计时期考虑到这些问题并加以克服,系统才能够更加健壮。

猜你喜欢

转载自juejin.im/post/7017682266973274119