缓存穿透
那么啥又是缓存穿透呢?
缓存穿透是指一个请求要访问的数据,缓存和数据库中都没有,而用户短时间、高密度的发起这样的请求,每次都打到数据库服务上,给数据库造成了压力。
一般来说这样的请求属于恶意请求。
就比如说,我这是一个技术公众号,你明明知道我没有,但是你非要来我这里买一瓶啤酒,恶意请求。
怎么解决呢?
两个方案。
第一个缓存空对象。
就是在数据库即使没有查询到数据,我们也把这次请求当做 key 缓存起来,value 可以是 NULL。
下次同样请求就会命中这个 NULL,缓存层就处理了这个请求,不会对数据库产生压力。
这样实现起来简单,开发成本很低。
但这样随之而来的一个面试题必须要注意一下:
对于恶意攻击,请求的时候key往往各不相同,且只请求一次,那你要把这些 key 都缓存起来的话,因为每个 key 都只请求一次,那还是每次都会请求数据库,没有保护到数据库呀?
这个问题,布隆过滤器,了解一下?
关于布隆过滤器我之前写过这篇文章,可以看看:《布隆,牛逼!布谷鸟,牛逼!》
布隆过滤器的特性是说某个值存在时,这个值可能不存在。当它说不存在时,那就肯定不存在。
所以可以基于这个特性,把已有数据都构建到布隆过滤器里面去。
然后它可以帮忙挡住绝大部分的攻击。
但是还有个比较容易忽视的连环炮问题:
面试官:布隆过滤器容量有限且不支持删除,随着里面内容的增加,误判率就会随之上升。请问,这个问题你们是怎么解决的?
也是两个答题方向。
首先,不支持删除的话,就换一个支持删除的布隆过滤器的轮子咯。
比如我前面的文章中提到的布谷鸟过滤器。
或者就是提前重构布隆过滤器。
比如在容量达到 50% 的时候,就申请一个新的更大的布隆过滤器来替换掉之前的过滤器。
只是需要注意的是,重建你得知道有那些数据需要进行重建的,所以你得有个地方来记录。
比如就是 Redis、数据库,甚至内存缓存都可以。
没落地过没关系,你底气十足的回答就行了。
你要相信,面试官八成也没落地过,你们看的说不定都是同一份资料呢。
缓存雪崩
缓存雪崩是指缓存中大多数的数据在同一时间到达过期时间,而查询数据量巨大,这时候,又是缓存中没有,数据库中有的情况了。
请求都打到数据库上,引起数据库流量激增,压力瞬间增大,直接崩溃给你看。
和前面讲的缓存击穿不同的是,缓存击穿指大量的请求并发查询同一条数据。
缓存雪崩是不同数据都到了过期时间,导致这些数据在缓存中都查询不到,
雪崩,还是用的很形象的。
防止雪崩的方案简单来说就是错峰过期。
在设置 key 过期时间的时候,在加上一个短的随机过期时间,这样就能避免大量缓存在同一时间过期,引起的缓存雪崩。
如果发了雪崩,我们可以有服务降级、熔断、限流手段来拒绝一些请求,保证服务的正常。
但是,这些对用户体验是有一定影响的。假设我们的程序有这的逻辑,也是拿来兜底的,从用户的角度来说,是不希望走到这样的逻辑中去。
所以,还是以预防雪崩为主。
最后还有一种雪崩,就是整个 Redis 服务都挂了。
所以,接下来就要聊聊 Redis 服务的高可用架构了。
Redis 高可用架构
聊到 Redis 高可用架构,大家基本上都能想到主从、哨兵、集群这三种模式。
主从结构很简单,就不说了,其弊端主要是出现故障的时候需要人工介入干预,需要人工介入的,就不是真正的高可用。
哨兵和集群这两种是写在官网上的方案:
上面划线的话翻译过来就是:Redis 通过 Redis 哨兵(Sentinel)和 Cluster 集群提供高可用性(high availability)。
其中,哨兵是官方推荐的高可用方案(official high availability solution):
所以主要说一下哨兵模式。
哨兵是用来管理多个 Redis 服务器的,我从《Redis开发与运维》一书中,拍个照片给你看看:
它主要执行三种类型的任务:
- 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
- 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
- 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。
哨兵其实也是一个分布式系统,我们可以运行多个哨兵。
然后这些哨兵之间需要相互通气,交流信息,通过投票来决定是否执行自动故障迁移,以及选择哪个从服务器作为新的主服务器。
哨兵之间采用的协议是 gossip,是一种去中心化的协议,达成的是最终一致性,非常有意思,
之前写过的凤凰架构里面有关于 gossip 协议的介绍,可以看看:
https://icyfenix.cn/distribution/consensus/gossip.html
另外,如果主节点挂了,哨兵到底通过什么规则选择新的主节点,也就是选举过程大致是怎么样的,也偶现于面试环节。
我以前就被问到过,幸好当时背的熟练。
简单说一下规则,无它,背诵就完事了:
- 在挂了的主节点下挂的从节点中,被标记为主观下线、已断线、或者最后一次回复 PING 命令的时间大于五秒钟的从节点都没有资格参与选举。
- 在挂了的主节点下挂的从节点中,那些与挂了的主节点连接断开的时长超过 down-after 配置指定的时长十倍的从节点都没有资格参与选举。
- 经过上面这两轮淘汰之后,剩下来的从服务器中,选出复制偏移量(replication offset)最大的那个从服务器作为新的主服务器。如果复制偏移量不可用,或者从服务器的复制偏移量相同,那么带有最小运行 ID 的那个从服务器成为新的主服务器。
其实执行上面这些操作的,是一个哨兵。而我们的哨兵一般是三个以上,那么那个哨兵来执行这些操作呢?
其实这个哨兵也是需要从多个哨兵中被选举一个出来的,被选出来的这个哨兵就是领头哨兵(leader Sentinel)。
选举领头哨兵的时候,采取的是 Raft 算法。
至于哨兵模式的搭建,一般来说是运维干的事儿。
但是网上的搭建教程很多,能自己跟着教程亲自搭一波那就更好了。
相信我,搭建的过程中你一定会碰到各种各样的问题,而这些问题就是你的收获。