超高并发下,Redis热点数据风险破解
1 介绍
作者是互联网一线研发负责人,所在业务也是业内核心流量来源,经常参与 业务预定、积分竞拍、商品秒杀等工作。
近期参与多场新员工的面试工作,经常就 『超高并发场景下热点数据』 可用性保障与候选人进行讨论。
本文聚焦一些关键点技术进行讨论,并总结一些热点场景的处理经验。
2 业务基础架构简图(假设)
3 超高并发下热点数据的稳定性保障
3.1 命题背景
1000w+请求同时投向后端,如果缓存未建立、失效,甚至缓存服务故障,就会透过缓存层直接投向数据库。
可能会造成整体击穿/雪崩,怎么破?
3.2 各种业务场景及应对方案
3.2.1 规律性热点数据预热
无论是聚集式热key,还是散列式热key,只要是有一定规律性的,均可以做 预热。
既然是热Key,那就想办法尽可能让它不进入MySQL,就不会对数据库造成伤害,。
这种场景最常见的就是对一些字典数据做预热,因为他们不容易改变,修改频次较低,但又很容易在高峰期被群蜂请求(突发式的批量请求)。
电商领域比如: 商品种类、品牌类型、折扣规则。
办公/教学领域比如:学校、年段、班级、学科、考试科目等。
一般来说如果10点是峰值期,那么可以预先在8~10点期间,可以逐渐的把大部分缓存建立起来。如图:
3.2.2 非规律性热点数据预热
Redis + 应用层 加探测器,预判热Key,并将探测到的热Key进行预热。
1、baidu实时热搜
2. taobao商品排行
这种额外的开销就是有一个实时计算的独立组件,因为热点新闻、热点数据都有急剧突变的特性。比如weibo多次因为突发热点新闻导致网站崩溃。
3.2.3 破解过期时间一致性问题
缓存的建立过程都是散列的,但是如果长时间静待都会被逐渐释放。
比如钉钉、飞书的办公场景,遇到夜晚低峰期、周末节假日,缓存Key被逐步释放之后。很容易在第二个工作日的早高峰造成大量创建缓存,流量井喷。
解决方案除了前面我们提到的缓存预热之外,错峰过期时间也是常规操作。
可以给缓存设置过期时间时加上一个随机值时间,使得每个key的过期时间分布开来,不会集中在同一时刻失效。
随机值我们团队的做法是:n * 3/4 + n * random() 。所以,比如你原本计划对一个缓存建立的过期时间为8小时,那就是6小时 + 0~2小时的随机值。
这样保证了均匀分布在 6~8小时之间。如图:
3.2.4 过滤垃圾请求
一般情况下,我们取数先从缓存中Get Key
,不存在的时候再从数据库中去获取,但这很容易给攻击者提供漏洞。
他可以疯狂模拟一些不存在的Key
,让你进入数据库去取数,这样就可以拖垮你的数据库,实现击溃你系统的目的。
有效的办法是在服务层先判断这个Key
的是否符合标准(比如滴滴的订单数据缓存包含时间戳+用户ID的序列化),这样可以过滤一部分无效攻击。
但是如果他能够破解你key的规则,依旧可以钻漏洞。你可以在缓存层上加一层过滤器,帮你Filter掉那些不合理的攻击。
详细可以参考我这篇《Redis系列16:聊聊布隆过滤器(原理篇) 》
3.2.5 消息队列和削峰
如果一个缓存不存在(不存在、过期、被误删都有可能),但是同时有千万请求投奔过来。
这时候关心是不是及时拿回正确数据已经不重要了,保住你的缓存和数据库不被击穿才是关键。
队列的目的是让并行变成串行,这一定程度上降低系统处理用户请求的吞吐能力,但是却能很好的缓解你服务的压力和风险。
如上图:第一个请求B从数据库中取,后面的C、A就是从缓存服务中取了,压力变小很多。
3.2.6 适当加锁
分布式锁场景,在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。
这种现象是多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个 互斥锁来锁住它。
其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存。
锁不好的地方就是在其他线程在拿不到锁的时候就等待,这个会造成系统整体吞吐量降低,用户体验度也不好。
这算是一种简单明了的降级策略了。
3.2.7 限流策略
一样是一种在流量井喷时保住服务不雪崩的有效方法,限流一般是从服务层去实现的。
Java服务的话可以使用 Hystrix进行限流 + 降级 ,比如一下子来了1W个请求,超过当前系统的吞吐承受能力,假设单秒TPS的能力只能是 5000个,那么剩余的 5000 请求就可以走限流逻辑。
可以设置一些默认值,然后调用我们自己降级逻辑去FallBack,保护最后的 MySQL 不会被大量的请求挂起。 除了Hystrix之外,阿里的Sentinel 和 Google的RateLimiter 都是不错的选择。
Sentinel 漏桶算法
RateLimiter 令牌桶算法
3.2.8 降级策略(备选缓存)
你的缓存层存在主备场景,他们之间定时异步同步,所以允许存在短暂数据不一致的情况。
当你的主服务挂了之后,降级去读备服务,数据时效性没那么高,但是也避免了数据库被打穿的情况发生。
3.2.9 降级策略(客户端缓存)
参考Redis 6.0的 Client Side Cache,看我这篇《追求性能极致:客户端缓存带来的革命》。
类似4.5做法,客户端缓存时效性会差一点,毕竟存在订阅跟同步的过程,数据没那么新。但是避免大量的请求直接上缓存服务,又因无效的缓存服务又把压力转移给数据库。
3.2.10 降级策略之空初始值
这是一种短效的降级方式:
如果一个缓存失效的时候,有无数个请求狂奔而来,而第一个请求从进入缓存池,判空,再到数据库检索,再查询出结果并返回设置缓存的这个过程里,缓存是不存在的。
这个就很危险,超高并发下这个短暂的过程足已让千千万万请求投向数据库。更别提这可能是个慢查询,整个过程可能长达2s以上,那对数据库是一种非常大的伤害。
业内有一种做法叫做空初始值,短暂的局部降级来保证整个数据库系统不被击穿。大概流程如下:
可以看出,整个过程中我们牺牲了A、B、C、D的请求,他们拿回了一个空值或者默认值,但是这局部的降级却保证整个数据库系统不被拥堵的请求击穿。
3.2.11 高可用集群和自动扩缩容
集群模式和自动扩缩容模式从服务到缓存到数据层都应该具备,否则无法根据流量来进行弹性伸缩,保持高可用。
如下图, 蓝色部件是扩容的部分,每一分层都有自己的动态扩容机制。
详细可以参考笔者这几篇文章。
《云原生:使用HPA和VPA实现集群扩缩容》
《数据库系列:数据库高可用及无损扩容》
3.2.12 雪崩之后的恢复
如果最终导致了缓存雪崩,那么重启后快速的数据恢复也是我们核心的目标。
刚刚恢复重启的缓存服务,这时候数据都是空的,大量的请求流量带来的缓存重建(进而拉动数据库流量)势必会带来压力甚至二次雪崩。
这时候最好的办法就是能够有工具进行缓存恢复,而不是从数据库中去获取数据来重建,这样的过程漫长而负重。
这块可以参考笔者的这两篇文章:
《Redis系列:RDB内存快照提供持久化能力》
《Redis稳定性之战:AOF日志支撑数据持久化 》
4 总结
扩展阅读:缓存雪崩、击穿、穿透
《架构与思维:一次缓存雪崩的灾难复盘 》
《架构与思维:再聊缓存击穿,面试是一场博弈》
超高并发下,Redis热点数据风险破解的更多相关文章
- 如何使redis中存放的都是热点数据?
当redis使用的内存超过设置的最大内存时,会触发redis的key淘汰机制,在redis3.0中的6中淘汰策略如下: (1)noeviction :不删除策略.当达到最大内存限制时,如果需要使用更多 ...
- 缓存雪崩、穿透如何解决,如何确保Redis只缓存热点数据?
缓存雪崩如何解决? 缓存穿透如何解决? 如何确保Redis缓存的都是热点数据? 如何更新缓存数据? 如何处理请求倾斜? 实际业务场景下,如何选择缓存数据结构 缓存雪崩 缓存雪崩简单说就是所有请求都从缓 ...
- 如何保证redis数据都是热点数据
mySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据? 1.限定 Redis 占用的内存,Redis 会根据自身数据淘汰策略,加载热数据到内存.所以,计算 ...
- 在这个应用中,我使用了 MQ 来处理异步流程、Redis 缓存热点数据、MySQL 持久化数据,还有就是在系统中调用另外一个业务系统的接口,对我的应用来说这些都是属于 RPC 调用,而 MQ、MySQL 持久化的数据也会存在于一个分布式文件系统中,他们之间的调用也是需要用 RPC 来完成数据交互的。
在这个应用中,我使用了 MQ 来处理异步流程.Redis 缓存热点数据.MySQL 持久化数据,还有就是在系统中调用另外一个业务系统的接口,对我的应用来说这些都是属于 RPC 调用,而 MQ.MySQ ...
- 如何保证redis中存放的都是热点数据
当redis使用的内存超过了设置的最大内存时,会触发redis的key淘汰机制,在redis 3.0中有6种淘汰策略: noeviction: 不删除策略.当达到最大内存限制时, 如果需要使用更多内存 ...
- Redis缓存何以一枝独秀?(2) —— 聊聊Redis的数据过期、数据淘汰以及数据持久化的实现机制
大家好,又见面了. 本文是笔者作为掘金技术社区签约作者的身份输出的缓存专栏系列内容,将会通过系列专题,讲清楚缓存的方方面面.如果感兴趣,欢迎关注以获取后续更新. 上一篇文章中呢,我们简单的介绍了下Re ...
- Java--缓存热点数据,最近最少使用算法
1.最近最少使用算法LRU (Least recently used,最近最少使用) [实现]:最常见的是使用一个链表保存缓存数据 1.新数据插入到链表头部: 2.每当缓存命中(即缓存数据被访问),将 ...
- Spring Boot使用redis做数据缓存
1 添加redis支持 在pom.xml中添加 <dependency> <groupId>org.springframework.boot</groupId> & ...
- Redis热点Key发现及常见解决方案!
一.热点Key问题产生的原因 1.用户消费的数据远大于生产的数据(热卖商品.热点新闻.热点评论.明星直播). 在日常工作生活中一些突发的的事件,例如:双十一期间某些热门商品的降价促销,当这其中的某一件 ...
- redis之数据操作详解
redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set ...
随机推荐
- NC23482 小A的最短路
题目链接 题目 题目描述 小A这次来到一个景区去旅游,景区里面有N个景点,景点之间有N-1条路径.小A从当前的一个景点移动到下一个景点需要消耗一点的体力值.但是景区里面有两个景点比较特殊,它们之间是可 ...
- eclipse项目右击找不到build path
右击项目–>properties–>Project Facets–>勾选右侧的Java,然后保存. 此时再操作就有了.
- MyBaits查询MySQL日期类型结果相差8个小时
问题描述 在Java项目中使用MyBatis作为ORM框架,但是查询出的MySQL日期类型字段值总是比数据库表里的值多8个小时. 具体说明: MySQL数据库表字段类型为timestamp,映射的Ja ...
- Git 分支管理参考模型
一个值得参考的Git分支管理模型如下: master 生产主分支,发布到生产环境使用这个分支,由hotfix或者release分支合并过来,不直接提交代码. release 预发布分支, 基于feat ...
- Generating equals/hashCode implementation but without a call to superclass
Generating equals/hashCode implementation but without a call to superclass1.lombok 警告,没有注入父类的字段当我们给一 ...
- 记一个 Andorid 生成文件失败的bug
Android生成文件失败:java.lang.IllegalStateException:Failed to build unique file: /storage/emulated/0/... 1 ...
- java+mysql实现的公益管理系统
一功能 1.管理员的登录 2.公益项目的增删改查 3.负责人的增删改查 4.捐款人的增删改查 5.志愿者增删改查 二界面展示 1.欢迎界面 2.登录界面 3.系统首页 4.项目管理 5.负责人管理 6 ...
- java+mysql数据库实现的学生管理系统
说明: java+mysql数据库实现的学生管理系统 功能 实现增加学生.删除学生.修改学生.学生列表.查询学生功能 截图: 开发工具/技术 java eclipse 价格:50元,有需要联系 微信 ...
- 在MATPool矩池云完成Pytorch训练MNIST数据集
本文为矩池云入门手册的补充:Pytorch训练MNIST数据集代码运行过程. 案例代码和对应数据集,以及在矩池云上的详细操作可以在矩池云入门手册中查看,本文基于矩池云入门手册,默认用户已经完成了机器租 ...
- 在Ubuntu搭建DHCP服务器
一.提供DHCP的服务器,自己必须有固定的IP地址 不然局域网就乱了,服务器自身启动(比如搭建完DHCP服务后,重新启动了服务器)的时候,DHCP服务器没有IP地址,无法和自己的DHCP服务通信. 在 ...