内存错误、服务器停电等都会致使服务器宕机,此时节点没法正常工做,称为不可用。程序员
服务器宕机会致使节点失去全部内存信息,所以须要将内存信息保存到持久化介质上。算法
有一种特殊的网络异常称为——网络分区 ,即集群的全部节点被划分为多个区域,每一个区域内部能够通讯,可是区域之间没法通讯。sql
磁盘故障是一种发生几率很高的异常。数据库
使用冗余机制,将数据存储到多台服务器。缓存
在分布式系统中,一个请求除了成功和失败两种状态,还存在着超时状态。服务器
能够将服务器的操做设计为具备 幂等性 ,即执行屡次的结果与执行一次的结果相同。若是使用这种方式,当出现超时的时候,能够不断地从新请求直到成功。网络
常见的性能指标有:吞吐量、响应时间。架构
其中,吞吐量指系统在某一段时间能够处理的请求总数,一般为每秒的读操做数或者写操做数;响应时间指从某个请求发出到接收到返回结果消耗的时间。并发
这两个指标每每是矛盾的,追求高吞吐的系统,每每很难作到低响应时间,解释以下:负载均衡
可用性指系统在面对各类异常时能够提供正常服务的能力。能够用系统可用时间占总时间的比值来衡量,4 个 9 的可用性表示系统 99.99% 的时间是可用的。
能够从两个角度理解一致性:从客户端的角度,读写操做是否知足某种特性;从服务器的角度,多个数据副本之间是否一致。
指系统经过扩展集群服务器规模来提升性能的能力。理想的分布式系统须要实现“线性可扩展”,即随着集群规模的增长,系统的总体性能也会线性增长。
分布式存储系统的数据分布在多个节点中,经常使用的数据分布方式有哈希分布和顺序分布。
数据库的水平切分(Sharding)也是一种分布式存储方法,下面的数据分布方法一样适用于 Sharding。
哈希分布就是将数据计算哈希值以后,按照哈希值分配到不一样的节点上。例若有 N 个节点,数据的主键为 key,则将该数据分配的节点序号为:hash(key)%N。
传统的哈希分布算法存在一个问题:当节点数量变化时,也就是 N 值变化,那么几乎全部的数据都须要从新分布,将致使大量的数据迁移。
一致性哈希
Distributed Hash Table(DHT):对于哈希空间 [0, 2n-1],将该哈希空间当作一个哈希环,将每一个节点都配置到哈希环上。每一个数据对象经过哈希取模获得哈希值以后,存放到哈希环中顺时针方向第一个大于等于该哈希值的节点上。
一致性哈希的优势是在增长或者删除节点时只会影响到哈希环中相邻的节点,例以下图中新增节点 X,只须要将数据对象 C 从新存放到节点 X 上便可,对于节点 A、B、D 都没有影响。
哈希分布式破坏了数据的有序性,顺序分布则不会。
顺序分布的数据划分为多个连续的部分,按数据的 ID 或者时间分布到不一样节点上。例以下图中,User 表的 ID 范围为 1 ~ 7000,使用顺序分布能够将其划分红多个子表,对应的主键范围为 1 ~ 1000,1001 ~ 2000,...,6001 ~ 7000。
顺序分布的优势是能够充分利用每一个节点的空间,而哈希分布很难控制一个节点存储多少数据。
可是顺序分布须要使用一个映射表来存储数据到节点的映射,这个映射表一般使用单独的节点来存储。当数据量很是大时,映射表也随着变大,那么一个节点就可能没法存放下整个映射表。而且单个节点维护着整个映射表的开销很大,查找速度也会变慢。为了解决以上问题,引入了一个中间层,也就是 Meta 表,从而分担映射表的维护工做。
衡量负载的因素不少,如 CPU、内存、磁盘等资源使用状况、读写请求数等。
分布式系统存储应当可以自动负载均衡,当某个节点的负载较高,将它的部分数据迁移到其它节点。
每一个集群都有一个总控节点,其它节点为工做节点,由总控节点根据全局负载信息进行总体调度,工做节点定时发送心跳包(Heartbeat)将节点负载相关的信息发送给总控节点。
一个新上线的工做节点,因为其负载较低,若是不加控制,总控节点会将大量数据同时迁移到该节点上,形成该节点一段时间内没法工做。所以负载均衡操做须要平滑进行,新加入的节点须要较长的一段时间来达到比较均衡的状态。
分布式系统不可能同时知足一致性(C:Consistency)、可用性(A:Availability)和分区容忍性(P:Partition Tolerance),最多只能同时知足其中两项。
一致性指的是多个数据副本是否能保持一致的特性。
在一致性的条件下,系统在执行数据更新操做以后可以从一致性状态转移到另外一个一致性状态。
对系统的一个数据更新成功以后,若是全部用户都可以读取到最新的值,该系统就被认为具备强一致性。
可用性指分布式系统在面对各类异常时能够提供正常服务的能力,能够用系统可用时间占总时间的比值来衡量,4 个 9 的可用性表示系统 99.99% 的时间是可用的。
在可用性条件下,系统提供的服务一直处于可用的状态,对于用户的每个操做请求老是可以在有限的时间内返回结果。
网络分区指分布式系统中的节点被划分为多个区域,每一个区域内部能够通讯,可是区域之间没法通讯。
在分区容忍性条件下,分布式系统在遇到任何网络分区故障的时候,仍然须要能对外提供一致性和可用性的服务,除非是整个网络环境都发生了故障。
在分布式系统中,分区容忍性必不可少,由于须要老是假设网络是不可靠的。所以,CAP 理论实际在是要在可用性和一致性之间作权衡。
可用性和一致性每每是冲突的,很难都使它们同时知足。在多个节点之间进行数据同步时,
BASE 是基本可用(Basically Available)、软状态(Soft State)和最终一致性(Eventually Consistent)三个短语的缩写。
BASE 理论是对 CAP 中一致性和可用性权衡的结果,它的理论的核心思想是:即便没法作到强一致性,但每一个应用均可以根据自身业务特色,采用适当的方式来使系统达到最终一致性。
指分布式系统在出现故障的时候,保证核心可用,容许损失部分可用性。
例如,电商在作促销时,为了保证购物系统的稳定性,部分消费者可能会被引导到一个降级的页面。
指容许系统中的数据存在中间状态,并认为该中间状态不会影响系统总体可用性,即容许系统不一样节点的数据副本之间进行同步的过程存在延时。
最终一致性强调的是系统中全部的数据副本,在通过一段时间的同步后,最终能达到一致的状态。
ACID 要求强一致性,一般运用在传统的数据库系统上。而 BASE 要求最终一致性,经过牺牲强一致性来达到可用性,一般运用在大型分布式系统中。
在实际的分布式场景中,不一样业务单元和组件对一致性的要求是不一样的,所以 ACID 和 BASE 每每会结合在一块儿使用。
两阶段提交(Two-phase Commit,2PC)
主要用于实现分布式事务,分布式事务指的是事务操做跨越多个节点,而且要求知足事务的 ACID 特性。
经过引入协调者(Coordinator)来调度参与者的行为,并最终决定这些参与者是否要真正执行事务。
准备阶段
协调者询问参与者事务是否执行成功,参与者发回事务执行结果。
提交阶段
若是事务在每一个参与者上都执行成功,事务协调者发送通知让参与者提交事务;不然,协调者发送通知让参与者回滚事务。
须要注意的是,在准备阶段,参与者执行了事务,可是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。
同步阻塞
全部事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,没法进行其它操做。
单点问题
协调者在 2PC 中起到很是大的做用,发生故障将会形成很大影响,特别是在阶段二发生故障,全部参与者会一直等待状态,没法完成其它操做。
数据不一致
在阶段二,若是协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
太过保守
任意一个节点失败就会致使整个事务失败,没有完善的容错机制。
优势:尽可能保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能 100%保证强一致) 缺点:实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景。
补偿事务(TCC)其核心思想是:针对每一个操做,都要注册一个与其对应的确认和补偿(撤销)操做。它分为三个阶段:
举个例子,假设 Bob 要向 Smith 转帐,思路大概是:
本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操做知足事务特性。
这种方案遵循 BASE 理论,采用的是最终一致性。
本地消息表利用了本地事务来实现分布式事务,而且使用了消息队列来保证最终一致性。
有一些第三方的 MQ 是支持事务消息的,好比 RocketMQ,他们支持事务消息的方式也是相似于采用的二阶段提交。可是市面上一些主流的 MQ 都是不支持事务消息的,好比 RabbitMQ 和 Kafka 都不支持。
以阿里的 RocketMQ 中间件为例,其思路大体为:
也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息。若是确认消息发送失败了 RocketMQ 会按期扫描消息集群中的事务消息,这时候发现了 Prepared 消息,它会向消息发送者确认,因此生产方须要实现一个 check 接口,RocketMQ 会根据发送端设置的策略来决定是回滚仍是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。
用于达成共识性问题,即对多个节点产生的值,该算法能保证只选出惟一一个值。
主要有三类节点:
算法须要知足 safety 和 liveness 两方面的约束要求(实际上这两个基础属性是大部分分布式算法都该考虑的):
safety:保证决议结果是对的,无歧义的,不会出现错误状况。
liveness:保证决议过程能在有限时间内完成。
基本过程包括 proposer 提出提案,先争取大多数 acceptor 的支持,超过一半支持时,则发送结案结果给全部人进行确认。一个潜在的问题是 proposer 在此过程当中出现故障,能够经过超时机制来解决。极为凑巧的状况下,每次新的一轮提案的 proposer 都刚好故障,系统则永远没法达成一致(几率很小)。
Paxos 能保证在超过 $1/2$ 的正常节点存在时,系统能达成共识。
若是系统中限定只有某个特定节点是提案者,那么一致性确定能达成(只有一个方案,要么达成,要么失败)。提案者只要收到了来自多数接收者的投票,便可认为经过,由于系统中不存在其余的提案。
但一旦提案者故障,则系统没法工做。
限定某个节点做为接收者。这种状况下,共识也很容易达成,接收者收到多个提案,选第一个提案做为决议,拒绝掉后续的提案便可。
缺陷也是容易发生单点故障,包括接收者故障或首个提案者节点故障。
以上两种情形其实相似主从模式,虽然不那么可靠,但由于原理简单而被普遍采用。
当提案者和接收者都推广到多个的情形,会出现一些挑战。
既然限定单提案者或单接收者都会出现故障,那么就得容许出现多个提案者和多个接收者。问题一会儿变得复杂了。
一种状况是同一时间片断(如一个提案周期)内只有一个提案者,这时能够退化到单提案者的情形。须要设计一种机制来保障提案者的正确产生,例如按照时间、序列、或者你们猜拳(出一个数字来比较)之类。考虑到分布式系统要处理的工做量很大,这个过程要尽可能高效,知足这一条件的机制很是难设计。
另外一种状况是容许同一时间片断内能够出现多个提案者。那同一个节点可能收到多份提案,怎么对他们进行区分呢?这个时候采用只接受第一个提案而拒绝后续提案的方法也不适用。很天然的,提案须要带上不一样的序号。节点须要根据提案序号来判断接受哪一个。好比接受其中序号较大(每每意味着是接受新提出的,由于旧提案者故障几率更大)的提案。
如何为提案分配序号呢?一种可能方案是每一个节点的提案数字区间彼此隔离开,互相不冲突。为了知足递增的需求能够配合用时间戳做为前缀字段。
此外,提案者即使收到了多数接收者的投票,也不敢说就必定经过。由于在此过程当中系统可能还有其它的提案。
Raft 算法是 Paxos 算法的一种简化实现。
包括三种角色:leader、candidate 和 follower,其基本过程为:
注:此处 log 并不是是指日志消息,而是各类事件的发生记录。
有三种节点:Follower、Candidate 和 Leader。Leader 会周期性的发送心跳包给 Follower。每一个 Follower 都设置了一个随机的竞选超时时间,通常为 150ms~300ms,若是在这个时间内没有收到 Leader 的心跳包,就会变成 Candidate,进入竞选阶段。
缓存雪崩是指:在高并发场景下,因为原有缓存失效,新缓存未到期间(例如:咱们设置缓存时采用了相同的过时时间,在同一时刻出现大面积的缓存过时),全部本来应该访问缓存的请求都去查询数据库了,而对数据库 CPU 和内存形成巨大压力,严重的会形成数据库宕机。从而造成一系列连锁反应,形成整个系统崩溃。
解决方案:
缓存失效时产生的雪崩效应,将全部请求所有放在数据库上,这样很容易就达到数据库的瓶颈,致使服务没法正常提供。尽可能避免这种场景的发生。
缓存穿透是指:用户查询的数据,在数据库没有,天然在缓存中也不会有。这样就致使用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,而后返回空(至关于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是常常提的缓存命中率问题。
当在流量较大时,出现这样的状况,一直请求 DB,很容易致使服务挂掉。
解决方案:
缓存预热这个应该是一个比较常见的概念,相信不少小伙伴都应该能够很容易的理解,缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就能够避免在用户请求的时候,先查询数据库,而后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
解决方案:
除了缓存服务器自带的缓存失效策略以外(Redis 默认的有 6 中策略可供选择),咱们还能够根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
二者各有优劣,第一种的缺点是维护大量缓存的 key 是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪一种方案,你们能够根据本身的应用场景来权衡。
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然须要保证服务仍是可用的,即便是有损服务。系统能够根据一些关键数据进行自动降级,也能够配置开关实现人工降级。
降级的最终目的是保证核心服务可用,即便是有损的。并且有些服务是没法降级的(如加入购物车、结算)。
本文的重点是你有没有收获与成长,其他的都不重要,但愿读者们能谨记这一点。同时我通过多年的收藏目前也算收集到了一套完整的学习资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、Jvm性能调优、Spring,MyBatis,Nginx源码分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql、Zookeeper、Tomcat、Docker、Dubbo、Nginx等多个知识点高级进阶干货,但愿对想成为架构师的朋友有必定的参考和帮助