首页 > 数据库 >超高并发下,Redis热点数据风险破解

超高并发下,Redis热点数据风险破解

时间:2024-03-21 09:23:09浏览次数:32  
标签:降级 缓存 请求 数据库 Redis 并发 3.2 超高 key

Redis24篇集合

1 介绍

作者是互联网一线研发负责人,所在业务也是业内核心流量来源,经常参与 业务预定、积分竞拍、商品秒杀等工作。
近期参与多场新员工的面试工作,经常就 『超高并发场景下热点数据』 可用性保障与候选人进行讨论。
本文聚焦一些关键点技术进行讨论,并总结一些热点场景的处理经验。

2 业务基础架构简图(假设)

image

3 超高并发下热点数据的稳定性保障

3.1 命题背景

1000w+请求同时投向后端,如果缓存未建立、失效,甚至缓存服务故障,就会透过缓存层直接投向数据库。
可能会造成整体击穿/雪崩,怎么破?

3.2 各种业务场景及应对方案

3.2.1 规律性热点数据预热

无论是聚集式热key,还是散列式热key,只要是有一定规律性的,均可以做 预热
既然是热Key,那就想办法尽可能让它不进入MySQL,就不会对数据库造成伤害,。
这种场景最常见的就是对一些字典数据做预热,因为他们不容易改变,修改频次较低,但又很容易在高峰期被群蜂请求(突发式的批量请求)。
电商领域比如: 商品种类、品牌类型、折扣规则。
办公/教学领域比如:学校、年段、班级、学科、考试科目等。

一般来说如果10点是峰值期,那么可以预先在8~10点期间,可以逐渐的把大部分缓存建立起来。如图:
image

3.2.2 非规律性热点数据预热

Redis + 应用层 加探测器,预判热Key,并将探测到的热Key进行预热。
1、baidu实时热搜
image
2. taobao商品排行
image
这种额外的开销就是有一个实时计算的独立组件,因为热点新闻、热点数据都有急剧突变的特性。比如weibo多次因为突发热点新闻导致网站崩溃。

3.2.3 破解过期时间一致性问题

缓存的建立过程都是散列的,但是如果长时间静待都会被逐渐释放。
比如钉钉、飞书的办公场景,遇到夜晚低峰期、周末节假日,缓存Key被逐步释放之后。很容易在第二个工作日的早高峰造成大量创建缓存,流量井喷。
解决方案除了前面我们提到的缓存预热之外,错峰过期时间也是常规操作。
可以给缓存设置过期时间时加上一个随机值时间,使得每个key的过期时间分布开来,不会集中在同一时刻失效。
随机值我们团队的做法是:n * 3/4 + n * random() 。所以,比如你原本计划对一个缓存建立的过期时间为8小时,那就是6小时 + 0~2小时的随机值。
这样保证了均匀分布在 6~8小时之间。如图:
image

3.2.4 过滤垃圾请求

一般情况下,我们取数先从缓存中Get Key,不存在的时候再从数据库中去获取,但这很容易给攻击者提供漏洞。
他可以疯狂模拟一些不存在的Key,让你进入数据库去取数,这样就可以拖垮你的数据库,实现击溃你系统的目的。
有效的办法是在服务层先判断这个Key的是否符合标准(比如滴滴的订单数据缓存包含时间戳+用户ID的序列化),这样可以过滤一部分无效攻击。
但是如果他能够破解你key的规则,依旧可以钻漏洞。你可以在缓存层上加一层过滤器,帮你Filter掉那些不合理的攻击。
详细可以参考我这篇《Redis系列16:聊聊布隆过滤器(原理篇)
image

3.2.5 消息队列和削峰

如果一个缓存不存在(不存在、过期、被误删都有可能),但是同时有千万请求投奔过来。
这时候关心是不是及时拿回正确数据已经不重要了,保住你的缓存和数据库不被击穿才是关键。
队列的目的是让并行变成串行,这一定程度上降低系统处理用户请求的吞吐能力,但是却能很好的缓解你服务的压力和风险。
image
如上图:第一个请求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 漏桶算法
image
RateLimiter 令牌桶算法
image

3.2.8 降级策略(备选缓存)

你的缓存层存在主备场景,他们之间定时异步同步,所以允许存在短暂数据不一致的情况。
当你的主服务挂了之后,降级去读备服务,数据时效性没那么高,但是也避免了数据库被打穿的情况发生。
image

3.2.9 降级策略(客户端缓存)

参考Redis 6.0的 Client Side Cache,看我这篇《追求性能极致:客户端缓存带来的革命》。
类似4.5做法,客户端缓存时效性会差一点,毕竟存在订阅跟同步的过程,数据没那么新。但是避免大量的请求直接上缓存服务,又因无效的缓存服务又把压力转移给数据库。
image

3.2.10 降级策略之空初始值

这是一种短效的降级方式:
如果一个缓存失效的时候,有无数个请求狂奔而来,而第一个请求从进入缓存池,判空,再到数据库检索,再查询出结果并返回设置缓存的这个过程里,缓存是不存在的。
这个就很危险,超高并发下这个短暂的过程足已让千千万万请求投向数据库。更别提这可能是个慢查询,整个过程可能长达2s以上,那对数据库是一种非常大的伤害。
业内有一种做法叫做空初始值,短暂的局部降级来保证整个数据库系统不被击穿。大概流程如下:
image
可以看出,整个过程中我们牺牲了A、B、C、D的请求,他们拿回了一个空值或者默认值,但是这局部的降级却保证整个数据库系统不被拥堵的请求击穿。

3.2.11 高可用集群和自动扩缩容

集群模式和自动扩缩容模式从服务到缓存到数据层都应该具备,否则无法根据流量来进行弹性伸缩,保持高可用。
如下图, 蓝色部件是扩容的部分,每一分层都有自己的动态扩容机制。
image
详细可以参考笔者这几篇文章。
云原生:使用HPA和VPA实现集群扩缩容
数据库系列:数据库高可用及无损扩容

3.2.12 雪崩之后的恢复

如果最终导致了缓存雪崩,那么重启后快速的数据恢复也是我们核心的目标。
刚刚恢复重启的缓存服务,这时候数据都是空的,大量的请求流量带来的缓存重建(进而拉动数据库流量)势必会带来压力甚至二次雪崩。
这时候最好的办法就是能够有工具进行缓存恢复,而不是从数据库中去获取数据来重建,这样的过程漫长而负重。
这块可以参考笔者的这两篇文章:
Redis系列:RDB内存快照提供持久化能力
Redis稳定性之战:AOF日志支撑数据持久化

4 总结

扩展阅读:缓存雪崩、击穿、穿透
架构与思维:一次缓存雪崩的灾难复盘
架构与思维:再聊缓存击穿,面试是一场博弈

标签:降级,缓存,请求,数据库,Redis,并发,3.2,超高,key
From: https://www.cnblogs.com/wzh2010/p/18030929

相关文章

  • Java并发编程:第四章 线程池(Executor框架)
    文章目录一、为什么需要线程池1、线程的创建2、线程销毁二、优点1、降低资源消耗2、提高响速度3、线程管理三、架构说明四、Executors工具类1、介绍2、Executors工具类常用的方法(1)newFixedThreadPool(intnThreads):(2)newCachedThreadPool()(3)newSingleThreadExecutor()......
  • CsRedis
    首先需要安装CSRedis包dotnetaddpackageCSRedis创建RedisClient对象,使用哪个库,密码,都可以在这里设置varredis=newRedisClient("localhost:6379");基本操作//写入数据redis.Set("key1","小明");//读取数据varname=redis.Get<string>("key1"......
  • 订单号规则,不能重复。redis去重 redis集合set应用
    订单号规则,不能重复。redis去重redis集合set应用redis锁定商品解决并发售卖问题RedisUtil工具类https://www.cnblogs.com/oktokeep/p/17917833.html需求背景:订单号根据日期反转加上随机数,订单号是否重复,前提是确保当天的订单号不重复,可以确保全局系统中的订单号不重复。//......
  • 接口性能指标-QPS-TPS-并发量
    1QPSQueriesPerSecond,每秒查询率,一台服务器每秒能够响应的查询次数。是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准,即每秒的响应请求数,也即是最大吞吐能力如何估算自己项目的QPS?使用日志估算即可,比如在中间件里记录访问日志,最终统计1s内有多少个访问,q......
  • JEP 462 结构化并发是一个很愚蠢的提案
    https://openjdk.org/jeps/462MotivationDevelopersmanagecomplexitybybreakingtasksdownintomultiplesubtasks.Inordinarysingle-threadedcode,thesubtasksexecutesequentially.However,ifthesubtasksaresufficientlyindependentofeachother,......
  • redis的客户端操作,使用场景
    ####配置允许远程链接############1使用配置文件启动redisdaemonizeyesbind0.0.0.0protected-modenorequirepass123456pidfile/var/run/redis.pidport6379dir"/root/redis-7.2.4/data"logfile6379.log#2redis-server./redis-conf#3允许远程链接......
  • redis的配置启动
    centos上安装redis#Redis是什么1C语言开源,非关系型数据库:早起版本2w3千行2基于键值对的存储系统:字典形式3多种数据结构:字符串,hash,列表,集合,有序集合4高性能(并发量高),功能丰富(不仅仅做缓存,还能做别的)#那些公司在用github,twitter,stackoverflow,阿里,百度,微博,美团,搜狐......
  • 【开发环境搭建篇】Redis客户端安装和配置
    作者介绍:本人笔名姑苏老陈,从事JAVA开发工作十多年了,带过大学刚毕业的实习生,也带过技术团队。最近有个朋友的表弟,马上要大学毕业了,想从事JAVA开发工作,但不知道从何处入手。于是,产生了写一个博客专栏想法,介绍当前互联网企业JAVA项目开发如何快速入门。本文收录于《30天企......
  • redis自学(22)Redis是单线程还是多线程?
    Redis是单线程还是多线程?Redis到底是单线程还是多线程? 如果仅仅聊Redis的核心的业务处理部分(命令处理),答案是单线程 如果是聊整个Redis那么答案是对线程在Redis版本迭代过程中,在两个重要的时间节点上引入了多线程的支持:Redisv4.0:引入多线程异步处理一些耗时较长的任务,......
  • 接口性能测试 —— Jmeter并发与持续性压测
    接口压测的方式:1、同时并发:设置线程组、执行时间、循环次数,这种方式可以控制接口请求的次数2、持续压测:设置线程组、循环次数,勾选“永远”,调度器(持续时间),这种方式可以控制压测周期时间指定并发数例1:设置线程数:10设置执行时间:0设置循环次数:5说明:使10个线程启动并同时运行也就......