Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

雪崩穿透击穿 #817

Open
guanhui07 opened this issue Dec 17, 2022 · 0 comments
Open

雪崩穿透击穿 #817

guanhui07 opened this issue Dec 17, 2022 · 0 comments
Labels

Comments

@guanhui07
Copy link
Owner

雪崩

雪崩概念
如果缓在某一个时刻出现大规模的key失效,
那么就会导致大量的请求打在了数据库上面,
导致数据库压力巨大,如果在高并发的情况下,
可能瞬间就会导致数据库宕机。
这时候如果运维马上又重启数据库,
马上又会有新的流量把数据库打死。
这就是缓存雪崩

为什么会发生雪崩
第一种是Redis宕机
第二种可能就是采用了相同的过期时间
雪崩解决方案

事前
均匀过期:设置不同的过期时间,让缓存失效的时间尽量均匀,
避免相同的过期时间导致缓存雪崩,造成大量数据库的访问。
分级缓存:第一级缓存失效的基础上,访问二级缓存,
每一级缓存的失效时间都不同。
热点数据缓存永远不过期
保证Redis缓存的高可用,防止Redis宕机导致缓存雪崩的问题。可以使用 主从+ 哨兵,
Redis集群来避免 Redis 全盘崩溃的情况
事中
互斥锁:在缓存失效后,通过互斥锁或者队列来
控制读数据写缓存的线程数量,
比如某个key只允许一个线程查询数据和写缓存,其他线程等待。这种方式会阻塞
其他的线程,此时系统的吞吐量会下降
使用熔断机制,限流降级。当流量达到一定的阈值,直接返回“系统拥挤”之类的提示,
防止过多的请求打在数据库上将数据库击垮,
至少能保证一部分用户是可以正常使用,
其他用户多刷新几次也能得到结果。
事后:
开启Redis持久化机制,尽快恢复缓存数据,一旦重启,就能从磁盘上自动
加载数据恢复内存中的数据。
永不过期实际包含两层意思:

物理不过期,针对热点key不设置过期时间
逻辑过期,把过期时间存在key对应的value里,如果发现要过期了,
通过一个后台的异步线程进行缓存的构建

击穿

击穿概念
缓存击穿跟缓存雪崩有点类似,缓存雪崩是大规模的key失效,
而缓存击穿是某个热点的key失效,大并发集中对其进行请求,
就会造成大量请求读缓存没读到数据,从而导致高并发访问数据库,
引起数据库压力剧增。这种现象就叫做缓存击穿

为什么会发生击穿
关键在于某个热点的key失效了,导致大并发集中打在数据库上。

解决方向
第一是否可以考虑热点key不设置过期时间
第二是否可以考虑降低打在数据库上的请求数量
解决方案

在缓存失效后,通过互斥锁或者队列来控制读数据写缓存的线程数量,
比如某个key只允许一个线程查询数据和写缓存,其他线程等待。
这种方式会阻塞其他的线程,此时系统的吞吐量会下降
热点数据缓存永远不过期。

穿透

穿透概念
缓存穿透是指用户请求的数据在缓存中不存在即没有命中,
同时在数据库中也不存在,导致用户每次
请求该数据都要去数据库中查询一遍。
如果有恶意攻击者不断请求系统中不存在的数据,
会导致短时间大量请求落在数据库上,
造成数据库压力过大,甚至导致数据库承受不住而宕机崩溃。

为什么会发生穿透
缓存穿透的关键在于在Redis中查不到key值,它和缓存击穿的根本区别在于
传进来的key在Redis中是不存在的。假如有黑客传进大量的不存在的key,
那么大量的请求打在数据库上是很致命的问题,
所以在日常开发中要对参数做好校验,
一些非法的参数,不可能存在的key就直接返回错误提示。

解决方案
将无效的key存放进Redis中:
当出现Redis查不到数据,数据库也查不到数据的情况,
我们就把这个key保存到Redis中,
设置value=“null”,并设置其过期时间极短,
后面再出现查询这个key的请求的时候,
直接返回null,就不需要再查询数据库了。
但这种处理方式是有问题的,假如传进来的这
个不存在的Key值每次都是随机的,那存进Redis也没有意义。
使用布隆过滤器:
如果布隆过滤器判定某个 key 不存在布隆过滤器中,那么就一定不存在,如果判定某个
key 存在,那么很大可能是存在(存在一定的误判率)。
于是我们可以在缓存之前再加
一个布隆过滤器,将数据库中的所有key都存储在布隆过滤器中,在查询Redis前先去
布隆过滤器查询 key 是否存在,如果不存在就直接返回,
不让其访问数据库,
从而避免了对底层存储系统的查询压力。
如何选择解决方案
针对一些恶意攻击,攻击带过来的大量key是随机,
那么我们采用第一种方案就会
缓存大量不存在key的数据。那么这种方案就不合适了,
我们可以先对使用布隆
过滤器方案进行过滤掉这些key。所以,
针对这种key异常多、请求重复率比较
低的数据,优先使用第二种方案直接过滤掉。而对于空数据的key有限的,
重复率比较高的,则可优先采用第一种方式进行缓存。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant