Redis缓存及热点key问题解决方案

今天又学到了很多,感觉雪崩和穿透很有意思理解起来也比较清晰,然后我搜索了一些资料,给自己做一个普及

我们通常使用 缓存 + 过期时间的策略来帮助我们加速接口的访问速度,减少了后端负载,同时保证功能的更新

缓存穿透

缓存系统,按照KEY去查询VALUE,当KEY对应的VALUE一定不存在的时候并对KEY并发请求量很大的时候,就会对后端造成很大的压力。

(查询一个必然不存在的数据。比如文章表,查询一个不存在的id,每次都会访问DB,如果有人恶意破坏,很可能直接对DB造成影响。)

由于缓存不命中,每次都要查询持久层。从而失去缓存的意义。

解决方法:

1、缓存层缓存空值。

–缓存太多空值,占用更多空间。(优化:给个空值过期时间)
–存储层更新代码了,缓存层还是空值。(优化:后台设置时主动删除空值,并缓存把值进去)

2、将数据库中所有的查询条件,放到布隆过滤器中。当一个查询请求来临的时候,先经过布隆过滤器进行检查,如果请求存在这个条件中,那么继续执行,如果不在,直接丢弃。

备注:

比如数据库中有10000个条件,那么布隆过滤器的容量size设置的要稍微比10000大一些,比如12000.

对于误判率的设置,根据实际项目,以及硬件设施来具体决定。但是一定不能设置为0,并且误判率设置的越小,哈希函数跟数组长度都会更多跟更长,那么对硬件,内存中间的要求就会相应的高。

private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, 0.0001);

有了size跟误判率,那么布隆过滤器就会产生相应的哈希函数跟数组。

综上:我们可以利用布隆过滤器,将redis缓存击穿控制在一个可容忍的范围内。

缓存雪崩(缓存失效)

如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。

缓存层宕掉后,流量会像奔逃的野牛一样,打向后端存储

解决方法:

  • 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
  • 可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存
  • 不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀
  • 做二级缓存,或者双缓存策略。A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。

热点key

(1) 这个key是一个热点key(例如一个重要的新闻,一个热门的八卦新闻等等),所以这种key访问量可能非常大。

(2) 缓存的构建是需要一定时间的。(可能是一个复杂计算,例如复杂的sql、多次IO、多个依赖(各种接口)等等)

于是就会出现一个致命问题:在缓存失效的瞬间,有大量线程来构建缓存(见下图),造成后端负载加大,甚至可能会让系统崩溃 。

解决方法:

1. 使用互斥锁(mutex key):这种解决方案思路比较简单,就是只让一个线程构建缓存,其他线程等待构建缓存的线程执行完,重新从缓存获取数据就可以了

2. "提前"使用互斥锁(mutex key):在value内部设置1个超时值(timeout1), timeout1比实际的memcache timeout(timeout2)小。当从cache读取到timeout1发现它已经过期时候,马上延长timeout1并重新设置到cache。然后再从数据库加载数据并设置到cache中。

3. "永远不过期":

这里的“永远不过期”包含两层意思:

(1) 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。

(2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期

4. 资源保护:可以做资源的隔离保护主线程池,如果把这个应用到缓存的构建也未尝不可。

四种方案对比:

作为一个并发量较大的互联网应用,我们的目标有3个:

1. 加快用户访问速度,提高用户体验。

2. 降低后端负载,保证系统平稳。

3. 保证数据“尽可能”及时更新(要不要完全一致,取决于业务,而不是技术。)

所以第二节中提到的四种方法,可以做如下比较,还是那就话:没有最好,只有最合适。

解决方案 优点 缺点
简单分布式锁(Tim yang)
1. 思路简单

2. 保证一致性


1. 代码复杂度增大

2. 存在死锁的风险

3. 存在线程池阻塞的风险

加另外一个过期时间(Tim yang)  1. 保证一致性 同上 
不过期(本文)
1. 异步构建缓存,不会阻塞线程池


1. 不保证一致性。

2. 代码复杂度增大(每个value都要维护一个timekey)。

3. 占用一定的内存空间(每个value都要维护一个timekey)。

资源隔离组件hystrix(本文)
1. hystrix技术成熟,有效保证后端。

2. hystrix监控强大。


1. 部分访问存在降级策略。

总结

1. 热点key + 过期时间 + 复杂的构建缓存过程 => mutex key问题

2. 构建缓存一个线程做就可以了。

3. 四种解决方案:没有最佳只有最合适。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • SpringBoot中Shiro缓存使用Redis、Ehcache的方法

    SpringBoot 中配置redis作为session 缓存器. 让shiro引用 本文是建立在你是使用这shiro基础之上的补充内容 第一种:Redis缓存,将数据存储到redis 并且开启session存入redis中. 引入pom <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifac

  • SpringBoot redis分布式缓存实现过程解析

    前言 应用系统需要通过Cache来缓存不经常改变得数据来提高系统性能和增加系统吞吐量,避免直接访问数据库等低速存储系统.缓存的数据通常存放在访问速度更快的内存里或者是低延迟存取的存储器,服务器上.应用系统缓存,通常有如下作用:缓存web系统的输出,如伪静态页面.缓存系统的不经常改变的业务数据,如用户权限,字典数据.配置信息等 大家都知道springBoot项目都是微服务部署,A服务和B服务分开部署,那么它们如何更新或者获取共有模块的缓存数据,或者给A服务做分布式集群负载,如何确保A服务的所有集群

  • 提高redis缓存命中率的方法

    缓存命中率的介绍 命中:可以直接通过缓存获取到需要的数据. 不命中:无法直接通过缓存获取到想要的数据,需要再次查询数据库或者执行其它的操作.原因可能是由于缓存中根本不存在,或者缓存已经过期. 通常来讲,缓存的命中率越高则表示使用缓存的收益越高,应用的性能越好(响应时间越短.吞吐量越高),抗并发的能力越强. 由此可见,在高并发的互联网系统中,缓存的命中率是至关重要的指标. 如何监控缓存的命中率 在memcached中,运行state命令可以查看memcached服务的状态信息,其中cmd_get表

  • Spring Cache手动清理Redis缓存

    这篇文章主要介绍了Spring Cache手动清理Redis缓存,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 注册cacheRedisTemplate 将 cache 的 RedisTemplate 注册为Bean @Bean(name = "cacheRedisTemplate") public RedisTemplate cacheRedisTemplate(@Qualifier("jedisConnectionFac

  • window手动操作清理redis缓存的技巧总结

    redis缓存知识点: 一.缓存穿透 缓存穿透是指查询一个缓存和数据库中都没有的数据,由于大部分缓存策略是被动加载的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义.用户不断发起请求,在流量大时,就可能对DB形成巨大的压力,利用不存在的key频繁攻击应用也是很大的问题. 二.缓存击穿 缓存击穿是指缓存中的一个热点Key(比如一个秒杀商品),在某个时间点过期的时候,恰好在这个时间点访问量剧增,对这个Key有大量的并发请求过

  • redis缓存穿透解决方法

    缓存技术可以用来减轻数据库的压力,提升访问效率.目前在企业项目中对缓存也是越来越重视.但是缓存不是说随随便便加入项目就可以了.将缓存整合到项目中,这才是第一步.而缓存带来的穿透问题,进而导致的雪蹦问题都是我们迫切需要解决的问题.本篇文章将我平时项目中的解决方案分享给大家,以供参考. 一.缓存穿透的原理 缓存的正常使用如图: 如图所示,缓存的使用流程: 1.先从缓存中取数据,如果能取到,则直接返回数据给用户.这样不用访问数据库,减轻数据库的压力. 2.如果缓存中没有数据,就会访问数据库. 这里面就

  • mybatis plus使用redis作为二级缓存的方法

    建议缓存放到 service 层,你可以自定义自己的 BaseServiceImpl 重写注解父类方法,继承自己的实现.为了方便,这里我们将缓存放到mapper层.mybatis-plus整合redis作为二级缓存与mybatis整合redis略有不同. 1. mybatis-plus开启二级缓存 mybatis-plus.configuration.cache-enabled=true 2. 定义RedisTemplate的bean交给spring管理,这里为了能将对象直接存取到redis中,

  • Redis缓存及热点key问题解决方案

    今天又学到了很多,感觉雪崩和穿透很有意思理解起来也比较清晰,然后我搜索了一些资料,给自己做一个普及 我们通常使用 缓存 + 过期时间的策略来帮助我们加速接口的访问速度,减少了后端负载,同时保证功能的更新 缓存穿透 缓存系统,按照KEY去查询VALUE,当KEY对应的VALUE一定不存在的时候并对KEY并发请求量很大的时候,就会对后端造成很大的压力. (查询一个必然不存在的数据.比如文章表,查询一个不存在的id,每次都会访问DB,如果有人恶意破坏,很可能直接对DB造成影响.) 由于缓存不命中,每次

  • 浅谈Redis 缓存的三大问题及其解决方案

    目录 一.缓存穿透 1. 常见解决方案 2. 布隆过滤器 3. 缓存空数据与布隆过滤器的比较 二.缓存击穿 解决方案 三.缓存雪崩 解决方案 Redis 经常用于系统中的缓存,这样可以解决目前 IO 设备无法满足互联网应用海量的读写请求的问题. 一.缓存穿透 缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起 id 为-1 的数据或者特别大的不存在的数据.有可能是黑客利用漏洞攻击从而去压垮应用的数据库. 1. 常见解决方案 对于缓存穿透问题,常见的解决方案有以下三种: 验证拦截:

  • Redis缓存穿透出现原因及解决方案

    在并发式的项目当中,一定要考虑一个缓存穿透的情况.那么什么是缓存穿透呢?简单的说来,就是当大量请求的key根本不在缓存当中,所以导致了请求直接到了数据库上,根本没有经过缓存这一层.比如一个黑客故意制造我们缓存中不存在的key发送大量的请求,就会导致请求直接落到数据库上. 也就是说,缓存穿透就是:1.缓存层不命中.2,存储层不命中,不将空的结果写回缓存.3,返回空结果给客户端. 一般mysql的默认最大连接数是150左右,当然这个是可以用show variables like '%max_conn

  • 面试分析分布式架构Redis热点key大Value解决方案

    目录 引言 1.面试官:你在项目中有没有遇到Redis热点数据问题,一般都是什么原因引起的? 2.面试官:真实项目中,那热点数据问题你是如何准确定位的呢? 3.如何解决热点数据问题 4.面试官:关于Redis最后一个问题,Redis支持丰富的数据类型,那么这些数据类型存储的大Value如何解决,线上有遇到这种情况吗? 总结 引言 关于 Redis 热点数据 & 大 key 大 value 问题也是容易被问的高阶问题,不如一次痛快点说完,让面试官无话可说,个人工作经验中,热点数据问题在工作中相比雪

  • 详解Redis缓存穿透/击穿/雪崩原理及其解决方案

    目录 1.简介 2.缓存穿透 2.1描述 2.2解决方案 3.缓存击穿 3.1描述 3.2解决方案 4.缓存雪崩 4.1描述 4.1解决方案 5.布隆过滤器 5.1描述 5.2数据结构 5.3"一定不在集合中" 5.4"可能在集合中" 5.5"删除困难" 5.6为什么不使用HashMap呢? 1. 简介 如图所示,一个正常的请求 1.客户端请求张铁牛的博客. 2.服务首先会请求redis,查看请求的内容是否存在. 3.redis将请求结果返回给服

  • 浅谈Redis缓存雪崩解决方案

    目录 1.保持缓存层的高可用 2.限流降级组件 3.缓存不过期 4.优化缓存过期时间 5.使用互斥锁重建缓存 6.异步重建缓存 缓存层承载着大量的请求,有效保护了存储层.但是如果由于大量缓存失效或者缓存整体不能提供服务,导致大量的请求到达存储层,会使存储层负载增加(大量的请求查询数据库) .这就是缓存雪崩的场景; 解决缓存雪崩可以从下面的几点着手: 1.保持缓存层的高可用 使用Redis哨兵模式或者Redis集群部署方式,即是个别Redis节点下线,整个缓存层依然可以使用.除此之外还可以在多个机

  • 浅谈Redis缓存击穿、缓存穿透、缓存雪崩的解决方案

    目录 前言 Redis缓存使用场景 Redis缓存穿透 解决方案 1.对空值缓存 2.添加参数校验 3.采用布隆过滤器 Redis缓存雪崩 解决方案 1.大量热点数据同时失效带来的缓存雪崩问题 2. 服务降级 3. Redis 缓存实例发生故障宕机带来的缓存雪崩问题 Redis缓存击穿 解决方案 1. 热key不过期 2. 分布式锁 总结 缓存击穿 缓存穿透 缓存雪崩 前言 在日常的项目中,缓存的使用场景是比较多的.缓存是分布式系统中的重要组件,主要解决在高并发.大数据场景下,热点数据访问的性能

  • 详解SpringBoot2.0的@Cacheable(Redis)缓存失效时间解决方案

    问题   @Cacheable注解不支持配置过期时间,所有需要通过配置CacheManneg来配置默认的过期时间和针对每个类或者是方法进行缓存失效时间配置. 解决   可以采用如下的配置信息来解决的设置失效时间问题 配置信息 @Bean public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { return new RedisCacheManager( RedisCacheWriter.no

  • Mysql和redis缓存不一致问题的解决方案

    目录 一.问题描述 二.解决方案 1.给缓存数据设置过期时间 2.缓存延时双删 3.删除缓存重试机制 4.读取biglog异步删除缓存 三.总结 一.问题描述 redis.mysql双写缓存不一致: 在更新缓存方面,对于更新完数据库,是更新缓存呢,还是删除缓存.又或者是先删除缓存,再更新数据库,其实大家存在很大的争议.于是博主战战兢兢,写了这篇文章. 二.解决方案 1.给缓存数据设置过期时间 先做一个说明,从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案.这种方案下,我们可以对存入缓

  • 详解redis缓存与数据库一致性问题解决

    数据库与缓存读写模式策略 写完数据库后是否需要马上更新缓存还是直接删除缓存? (1).如果写数据库的值与更新到缓存值是一样的,不需要经过任何的计算,可以马上更新缓存,但是如果对于那种写数据频繁而读数据少的场景并不合适这种解决方案,因为也许还没有查询就被删除或修改了,这样会浪费时间和资源 (2).如果写数据库的值与更新缓存的值不一致,写入缓存中的数据需要经过几个表的关联计算后得到的结果插入缓存中,那就没有必要马上更新缓存,只有删除缓存即可,等到查询的时候在去把计算后得到的结果插入到缓存中即可. 所

随机推荐