目录
一.缓存雪崩
缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。
一般有三种处理办法:
1.一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。
2.给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。
3.为key设置不同的缓存失效时间。
二.缓存穿透:
缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查询数据库,这也是经常提的缓存命中率问题。有很多种方法可以有效的解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但他的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样第二次到缓存中获取就有值了,而不会继续访问数据库。
三.缓存预热:缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
四.缓存更新:缓存更新除了缓存服务器自带的缓存失效策略之外(Redis默认的有6种策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
(1)定时去清理过期的缓存;
(2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
五.缓存降级
当访问量剧增,服务出现问题(如相应时间慢或者不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,既是是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车,结算)。
常见的限流方法:
固定窗口技术器:按照时间段划分窗口,有一次请求就加1,最为简单的算法,但这个算法有时会让通过请求量允许为限制的两倍。
滑动窗口计数器:通过将窗口再细分,并且按照时间“滑动”来解决突破限制的问题,但是时间区间的精度越高,算法所需的空间容量就越大。
漏桶:请求类似水滴,先放到桶里,服务的提供方则按照固定的速率从桶里面取出请求并执行。缺陷也很明显,当短时间内有大量的突发请求时,即便此时服务器没有任何负载,每个请求也都在队列中等待一段时间才能被响应。
令牌桶:往桶里发放令牌,每个请求过来之后拿走一个令牌,然后只处理有令牌的请求。令牌桶满了则多余的令牌会直接丢弃。令牌桶算法既能够将所有的请求平均分布到时间区间内,又能接受服务器能够承受范围内的突发请求,因此是目前使用较为广泛的一种限流算法。Google的开源项目guava提供了RateLimiter类,实现了单点的令牌桶限流。分布式环境下,可以考虑用Redis+Lua脚本实现令牌桶。如果请求量太大了,Redis也撑不住怎么办?我觉得可以类似于分布式ID的处理,Redis前面在增加预处理,比如每台及其预先申请一部分令牌,只有令牌用完之后才去Redis。如果还是太大,是否可以垂直切分?按照流量的来源,比如地理位置,IP之类的再拆开。
任务轮询或任务轮询+抢占排队方案
每个服务器首次启动时加入队列
每次任务运行首先判断自己是否是当前可运行任务,如果是便运行,如果不是当前运行的任务,检查自己是否在队列中,如果在,便退出,如果不在队列中,进入队列。
分布式系统通过副本控制协议,使得从系统外部读取系统内部各个副本的数据在一定的约束条件下相同,称之为副本一致性。副本一致性是针对分布式系统而言的,不是针对某一个副本而言。
强一致性:任何时刻任何用户或节点都可以读到最近一次成功更新的副本数据。强一致性是程度最高一致性要求,也是实践中最难以实现的一致性。
单调一致性:任何时刻,任何用户一旦读到某个数据在某次更新后的值,这个用户不会再读到比这个值更旧的值。单调一致性是弱于强一致性却非常实用的一种一致性级别。因为通常来说,用户只关心从己方视角观察到的一致性,而不会关注其他用户的一致性情况。
会话一致性:
任何用户在某一次会话内一旦读到某个数据在某次更新后的值,这个用户在这次会话过程中不会再读到比这个值更旧的值。会话一致性通过引入会话的概念,在单调一致性的基础上进一步放松约束,会话一致性只保证单个用户单次会话内数据的单调修改,对于不同用户间的一致性和同一用户不同会话间的一致性没有保障。实践中有许多机制正好对应会话的概念,例如php中的session概念。
最终一致性:最终一致性要求一旦更新成功,各个副本上的数据最终将达到完全一致的状态,但达到完全一致状态所需要的时间不能保障。对于最终一致性而言,一个用户只要始终读取某一个副本的数据,则可以实现类似单调一致性的效果,但一旦用户更换读取的副本,则无法保障任何一致性。
弱一致性:一旦某个更新成功,用户无法再一个确定时间内读到这次更新的值,且即使在某个副本上读到了新的值,也不能保证在其他副本上可以读到新的值。弱一致性系统一般很难再十几种使用,使用弱一致性系统需要应用方做更多的工作从而使得系统可用。
分布式算法? ? 一致性Hash算法
一致性Hash算法是个经典算法,Hash环的引入是为了解决单调性的问题;虚拟节点的引入是为了解决平衡性问题。
分布式算法? ? Paxos算法
Paxos算法是Lamport宗师提出的一种基于消息传递的分布式一致性算法,使其获得2013年图灵奖。自Paxos问世以来就持续垄断了分布式一致性算法,Paxos这个名词几乎等同于分布式一致性,很多分布式一致性算法都由Paxos演变而来。
分布式算法? ?Raft算法
Paxos是出了名的难懂,而Raft正是为了探索一种更易于理解的一致性算法而产生的。他的首要设计目的就是易于理解,所以在选主的冲突处理等方式上他都选择了非常简单明了的解决方案。
分布式算法? ZAB算法
ZAB协议全称:Zookeeper? Atomic
Broadcast(Zookeeper原子广播协议),他应该是所有一直选哪个协议中生产环境中应用最多的了。为什么呢?因为他是为了Zookeeper设计的分布式一致性协议。
? 判定哈希算法好坏的四个定义:
平衡性(Balance):平衡性是指害的结果能够尽可能分不到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。很多哈希算法都能够满足这一条件。
单调性(Monotonicity):单调性是指如果已经有一些内容通过哈希分派到了相应的缓存中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以映射到原有的或者新的缓冲中区,而不会被映射到旧的缓冲集合中的其他缓冲区。
分散性(Spread):在分布式环境中,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为他导致相同内容被存储到不同缓冲中去,降低了存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。
负载(Load):负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。