Redis系列(八):缓存到底该如何做到高可用?
作者:z小赵
★一枚用心坚持写原创的“无趣”程序猿,在自身受益的同时也让朋友们在技术上有所提升。
上篇文章分析比较了生产环境中常见的几种缓存,本文接着来分析分析缓存如何做到高可用,大白话解释下就是缓存到底该怎么做才能尽可能的避免缓存不可用的情况发生。
什么情况会导致缓存不可用?
单点问题:
什么是单点问题呢?就是我们在使用缓存时,有时候由于 QPS、内存容量只需要一个端口(此处的一个端口是指一主一从)就可以扛住所有读写请求时,比如写 QPS 5k,读 QPS 30k,内存 5G;根据这个数据规模,DBA 在部署资源时会选择只部署一个节点。当缓存资源由于某些情况导致服务器宕机或服务不可用时,由于只部署了一个端口,从而导致当前整个应用服务不可用(这种情况之前在实际生产环境中真实发生过,直接炸裂),这就是所谓的单点问题;其实在分布式架构中,为了做到服务的高可用,要尽量去避免单点问题,做到鸡蛋尽量不放在一个篮子里来尽量保证服务的可用性。
缓存穿透/缓存击穿
缓存穿透/缓存击穿是指某些时刻由于有些热 key 失效或者是过期,导致在一瞬间大量请求绕过缓存直接打到 DB 上,由于 DB 的读写 qps 并不高,从而导致 DB 直接瞬间被打挂,尤其是在微博这种应用上,突发热点流量加上缓存设计不合理导致热 key 过期,就会发生缓存击穿的情况。
缓存雪崩
缓存雪崩是指由于缓存过期时间设计不合理,缓存数据过期时间集中,导致在某一时刻缓存中数据大面积过期,从而导致缓存的 miss 率大大增加,由于缓存没有拦住请求导致大量请求落到 DB 上,最终导致后端 DB 资源被彻底打挂,即使重启 DB 也会被大量请求瞬间再次打挂。
如何设计高可用的缓存?
单点问题解决方案
首先,单端口在资源申请阶段部署为多端口的(大于一个端口),同时每个端口至少部署为一主一从且分布在不同的节点上;通过此种部署方式,会有如下几个好处:
假设某个从节点服务不可用时,将读请求转移到主节点上可继续提供服务,当从节点恢复后再加入到集群中继续提供服务。 假设集群中某几个节点服务不可用时,此时最坏的情况也只是影响部分用户的功能使用。
其次,在多端口部署的基础上,在机房层面做到多机房灾备;此种缓存的做法是:假设一个缓存部署两份,而这两份缓存部署在不同的机房中,这样的话,即使其中一个机房发生极端情况,可以将流量切换到另外一个机房中,从而做到机房层面的高可用。
最后,可以使用一致性 hash 算法,使用机器的 ip 或者域名做 hash 操作,分布在一个虚拟的环上,同样对于 key 也做相同的 hash 操作,当 key 得到一个 hash 值后,顺时针移动直到遇到第一个节点,然后将 key 对应的 value 存储到该节点上;此种做法是节点动态上下线的时候只需要对部分 key 做 rehash 操作,但是此种方案的缺点是:节点动态上下线后,key 经过 rehash 操作后导致各个节点数据分布不均,也就导致各个节点的服务承载的压力不均不可控。
缓存穿透/缓存击穿的解决方案
缓存击穿是由于热 key 失效导致的,所以第一种方案是:可以根据业务场景,选择可以将热 key 直接全部缓存到缓存中并且永远不过期,通过此种方式来避免热 key 因为过期的问题导致缓存击穿;第二种方案是:由于热 key 一般情况下并不是很多,所以我们可以考虑使用 localCache,在系统启动时,预先将热点数据全部加载到内存中,只要服务器不发生宕机就可以一直正常提供服务。
缓存雪崩的解决方案
缓存雪崩是在某一时刻,缓存中数据大量集中过期导致缓存不可用,对于此种情况,一般是在缓存 key 设计时,对 key 增加一个一定时间范围内的随机时间戳,使得 key 过期分散开来,从而避免所有 key 同时过期的尴尬情况。
总结
本文主要介绍了实际生产环境中缓存该如何做到高可用,从而避免由于缓存不可用而导致整个系统崩溃。
关于 Redis 系列文章总计 8 篇,从 Redis 基础命令使用,到 Redis 基础集合及常用命令的底层实现,然后到集群搭建实战,然后到 Redis 的线程模型,最后到线上缓存方案设计及缓存高可用的方案,到此关于 Redis 专题暂时告一段落,但后续还会继续更新关于缓存相关的文章,朋友们有对缓存相关知识感兴趣的话题可以和作者交流,后续会持续更新更多精彩的缓存文章。