缓存雪崩、缓存穿透解决方案以及实战

简介: 缓存雪崩和缓存穿透并不是一个概念缓存雪崩当缓存失效或缓存数据还没有准备就绪时,高并发请求接入时无法阻挡,从而接入数据库导致数据库宕机或者延迟,而数据库又被大量其他服务所依赖导致大面积服务崩溃,最终导致整个系统或网站的崩溃。解决方案:

缓存雪崩和缓存穿透并不是一个概念

缓存雪崩

当缓存失效或缓存数据还没有准备就绪时,高并发请求接入时无法阻挡,从而接入数据库导致数据库宕机或者延迟,而数据库又被大量其他服务所依赖导致大面积服务崩溃,最终导致整个系统或网站的崩溃。

解决方案:

1、分布式锁:只有一个线程能获得锁,获得后判断缓存数据是否存在,不存在获取缓存数据并更新缓存,存在获得数据,其他线程阻塞,更新缓存完成后或获得数据后释放锁

2、数据预热:提前将数据缓存好,不等用户请求再缓存数据,如果数据量大,启动后运维手动触发,数据量不大,启动自动加载

3、缓存双层降级策略:当缓存C1失效时,请求缓存备份C2,C1失效时间短,C2失效时间长

4、错峰更新缓存:更新缓存和用户请求高峰期错峰,定制更新策略

5、设置不同的过期时间,让缓存失效时间尽量均匀,这样不会大量用户同时缓存失效

缓存雪崩只对非常高并发的场景才会发生,一般流量的场景不会很严重,然后缓存穿透对一般流量的场景的影响也非常严重

缓存穿透

没有命中一个数据(没查到),导致再查还是没查到直接将大量请求放到数据库上

解决方案:

1、缓存空数据,并设置合适的过期时间以更新缓存

2、布隆过滤器排除不可能有缓存值的请求

布隆过滤器:将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力

网络异常,图片无法展示
|

一条很长的信息通过多个hash函数将其转化为一个bit向量,根据向量的结果判断这条信息是否存在。

优点:

  1. 省空间:位很省空间,一个很长的东西就变成了几位
  2. 省时间:判断、操作位很快,hash算法转化成位向量也很快

缺点:牺牲了一定的准确度

以下是guava实现的布隆过滤器的使用方法:

private BloomFilter<String> bf;
    //创建布隆过滤器(默认3%误差)
    bf = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), allUsers.size());
    bf.put(userDto.getUserName());
    if(!bf.mightContain(randomUser)){
    System.out.println("bloom filter don't has this user");
    return;
}

综合解决方案

说明:部分思路参考京东到家最佳实践

网络异常,图片无法展示
|

这个图中主要是分布式锁和缓存空值的方式来方式雪崩、穿透

这里主要说明一下分布式锁的细节:获得锁的线程才有机会更新缓存,而没有获得锁的线程在没有出现“死锁问题”的情况下只能等待第一个获得锁的线程将缓存数据准备好,直接享受缓存数据了那么有两种情况对锁的获取是不同的:

情况1:并发请求(多个请求)对应DB的查询对象是同一个

这种情况,并发请求可以竞争同一把锁,比如一个省的用户查询的配置信息都是一致的,那么这个key就可以以省为单位比如110config,一省用户的并发请求都去抢这个110config的锁

情况2:并发请求(多个请求)对应DB查询对象是多个(极端情况1对1)

这种情况,不可以所有并发请求都争抢一把锁,这样逻辑不正确,比如每个用户都有自己的订单数据,每个用户查询数据库的结果也都是不一样的,那么这个key就不能以省为单位,否则缓存的结果就肯定不正确

同样也不可以每个用户(并发请求时,每个用户一次请求,而不是每个用户多次请求)各自都有一把锁,假如1W人同时请求,有1W个请求,如果再设置1W把锁,缓存同时失效的话还是会同时有1W次请求放入DB,DB无法承受,那么这种情况可以让这1W人,争抢128个锁,抢到锁的请求,才有可能去更新缓存

//锁的数量 锁的数量越少 每个用户对锁的竞争就越激烈,直接打到数据库的流量就越少,对数据库的保护就越好,如果太小,又会影响系统吞吐量,可根据实际情况调整锁的个数
public static final String[] LOCKS = new String[128];
//在静态块中将128个锁先初始化出来
static {
    for (int i = 0; i < 128; i++) {
        LOCKS[i] = "lock_" + i;
    }
}
try{
  
  if(exists(cacheKey)){
    return cacheData;
  }
  String[] locks = OrderRedisKey.LOCKS;
//hash分组//缓存中没有,就先上锁,锁的粒度是根据用户Id的hashcode和127取模,这里是个hash的算法,相当于把userID均匀的分布于0-127之间
//a % (2^n) 等价于 a & (2^n - 1)   与运算的前提是2的n次方-1才与%等价,与运算比算术运算更快
int index = userId.hashCode() & (locks.length - 1);
lock(locks[index]);//竞争锁
if(exists(cacheKey)){
  return cacheData;
}
data=getFromDB();
if(data == null){
  //缓存空数据,并设定缓存过期时间
  addcahce(cacheKey,null,ttl_null);
}else{
  //缓存数据,并设定缓存过期时间
  addcahce(cacheKey,data,ttl_notnull);
}
return data;
}finally{
  unlock(locks[index])
}
相关文章
|
19天前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
这篇文章是关于如何在SpringBoot应用中整合Redis并处理分布式场景下的缓存问题,包括缓存穿透、缓存雪崩和缓存击穿。文章详细讨论了在分布式情况下如何添加分布式锁来解决缓存击穿问题,提供了加锁和解锁的实现过程,并展示了使用JMeter进行压力测试来验证锁机制有效性的方法。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
|
11天前
|
缓存 安全 Android开发
Android经典实战之用Kotlin泛型实现键值对缓存
本文介绍了Kotlin中泛型的基础知识与实际应用。泛型能提升代码的重用性、类型安全及可读性。文中详细解释了泛型的基本语法、泛型函数、泛型约束以及协变和逆变的概念,并通过一个数据缓存系统的实例展示了泛型的强大功能。
23 2
|
19天前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解、如何添加锁解决缓存击穿问题?分布式情况下如何添加分布式锁
这篇文章介绍了如何在SpringBoot项目中整合Redis,并探讨了缓存穿透、缓存雪崩和缓存击穿的问题以及解决方法。文章还提供了解决缓存击穿问题的加锁示例代码,包括存在问题和问题解决后的版本,并指出了本地锁在分布式情况下的局限性,引出了分布式锁的概念。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解、如何添加锁解决缓存击穿问题?分布式情况下如何添加分布式锁
|
11天前
|
缓存 NoSQL 数据库
go-zero微服务实战系列(五、缓存代码怎么写)
go-zero微服务实战系列(五、缓存代码怎么写)
|
21天前
|
缓存 数据库
缓存穿透和击穿
【8月更文挑战第16天】
32 0
缓存穿透和击穿
|
24天前
|
缓存 NoSQL Redis
一天五道Java面试题----第九天(简述MySQL中索引类型对数据库的性能的影响--------->缓存雪崩、缓存穿透、缓存击穿)
这篇文章是关于Java面试中可能会遇到的五个问题,包括MySQL索引类型及其对数据库性能的影响、Redis的RDB和AOF持久化机制、Redis的过期键删除策略、Redis的单线程模型为何高效,以及缓存雪崩、缓存穿透和缓存击穿的概念及其解决方案。
|
2月前
|
canal 缓存 NoSQL
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;先删除缓存还是先修改数据库,双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
|
9天前
|
缓存 NoSQL Java
惊!Spring Boot遇上Redis,竟开启了一场缓存实战的革命!
【8月更文挑战第29天】在互联网时代,数据的高速读写至关重要。Spring Boot凭借简洁高效的特点广受开发者喜爱,而Redis作为高性能内存数据库,在缓存和消息队列领域表现出色。本文通过电商平台商品推荐系统的实战案例,详细介绍如何在Spring Boot项目中整合Redis,提升系统响应速度和用户体验。
36 0
|
11天前
|
消息中间件 缓存 监控
go-zero微服务实战系列(六、缓存一致性保证)
go-zero微服务实战系列(六、缓存一致性保证)
|
23天前
|
存储 缓存 NoSQL
基于SpringBoot+Redis解决缓存与数据库一致性、缓存穿透、缓存雪崩、缓存击穿问题
这篇文章讨论了在使用SpringBoot和Redis时如何解决缓存与数据库一致性问题、缓存穿透、缓存雪崩和缓存击穿问题,并提供了相应的解决策略和示例代码。
51 0