节点是一个能够独立按照分布式协议完成一组逻辑的程序个体,工程中每每指进程。数据库
节点之间彻底独立互相隔离,通讯惟一方式是经过不可靠的网络。服务器
节点能够经过将数据写入与节点在同一台机器的本地存储设备保存数据网络
(1) 机器down机并发
大型集群每日down机发生几率0.1%,后果是该机器节点不能工做、重启后失去全部内存信息。tcp
(2) 网络异常分布式
消息丢失:网络拥塞、路由变更、设备异常、network partition(部分不正常)函数
消息乱序:IP存储转发、路由不肯定性、网络报文乱序微服务
数据错误:比特错误源码分析
不可靠TCP:到达协议栈以后与到达进程之间、忽然down机、分布式多个节点的tcp乱序性能
(3) 分布式系统的三态
任何请求都要考虑三种状况:成功、失败、超时。对于超时的请求,没法获知该请求是否被成功执行。
(4) 存储数据丢失
(5) 其余异常
IO操做缓慢、网络抖动、拥塞
replica/copy 指在分布式系统中为数据或服务提供的冗余:
数据副本:在不一样的节点上持久化同一份数据。例如GFS同一个chunk的数个副本
服务副本:数个节点提供相同的服务,服务不依赖本地存储,数据来自其余节点。例如Map Reduce的Job Worker
副本的consistency是针对分布式系统而言的,不是针对某一个副本而言。根据强弱程度分为:
强一致性:任什么时候刻任何用户/节点均可以读到最近一次更新成功的副本数据
单调一致性:任什么时候刻任何用户一旦读到某个数据某次更新后的值,就不会再读到更旧的值
会话一致性:任什么时候刻任何用户在某次会话内一旦读到某个数据某次更新后的值,就不会在此次会话再读到更旧的值
最终一致性:各个副本的数据最终将达到一致状态,但时间不保证
弱一致性:没有实用价值,略。
吞吐量:某一时间能够处理的数据总量
响应延迟:完成某一功能须要使用的时间
并发能力:QPS(query per second)
系统停服务的时间与正常服务的时间的比例
经过扩展集群机器提升系统性能、存储容量、计算能力的特性,是分布式系统特有的性质
副本带来的一致性问题
不管计算仍是存储,问题输入对象都是数据,如何拆分分布式系统的输入数据称为分布式系统的基本问题。
一种常见的哈希方式是按照数据属于的用户id计算哈希。
优势:
散列性:好
元信息:只须要函数+服务器总量
缺点:
可扩展性:差。一旦集群规模扩展,大多数数据都须要被迁移并从新分布
数据倾斜:当某个用户id的数据量异常庞大时,容易达到单台服务器处理能力的上限
将数据按特征值的值域范围划分数据。例如将用户id的值域分为[1, 33), [33, 90), [90, 100),由三台服务器处理。注意区间大小与区间内的数据大小没有关系。
优势:
可扩展性:好。灵活根据数据量拆分原有数据区间
缺点:
元信息:大。容易成为瓶颈。
与按范围分布数据方式相似,元信息容易成为瓶颈
(1) 以机器为节点
用一个hash函数计算数据(特征)的hash值,令该hash函数的值域成为一个封闭的环,将节点随机分布在环上。每一个节点负责处理从本身开始顺时针到下一节点的值域上的数据。
优势:
可扩展性:极好。任意动态添加、删除节点,只影响相邻节点
缺点:
元信息:大并且复杂
随机分布节点容易形成不均匀
动态增长节点后只能缓解相邻节点
一个接点异常时压力全转移到相邻节点
(2) 虚节点
虚节点,虚节点个数远大于机器个数,将虚节点均匀分布到值域环上,经过元数据找到真实机器节点。
优势:
某一个节点不可用致使多个虚节点不可用,均衡了压力
加入新节点致使增长多个虚节点,缓解了全局压力
前边4中数据分布方式的讨论中没有考虑数据副本的问题。
(1) 以机器为单位的副本
缺点:
恢复效率:低。假如1出问题,从2 3 中全盘拷贝数据较消耗资源,为避免影响服务通常会将2下线专门作拷贝,致使正常工做的副本只有3
可扩展性:差。假如系统3台机器互为副本,只增长两台机器的状况下没法组成新的副本组。
可用性:差。一台donw机,剩下两台压力增长50%。理想状况会均摊到整个集群,而不是单个副本组
(2) 以数据段为单位的副本
例如3台服务器,10G数据,按100hash取模获得100M每一个的数据段,每台服务器负责333个数据段。一旦将数据分红数据段,就能够以数据段为单位管理副本,副本与机器再也不硬相关。
例如系统中3个数据o p q, 每一个数据段有三个副本,系统中有4台机器:
优势:
恢复效率:高。能够整个集群同时拷贝
可用性:好。机器donw机的压力分散到整个集群
工程中彻底按照数据段创建副本会引发元数据开销增大,这种作法是将数据段组成一个大数据段。
若是计算节点和存储节点位于不一样的物理机器,开销很大,网络带宽会成为系统的瓶颈;将计算调度到与存储节点在同一台物理机器上的节点计算,称为计算本地化。
中心化副本控制协议的基本思路:由一个中心节点协调副本数据的更新、维护副本之间一致性,并发控制
并发控制:多个节点同时须要修改副本数据时,须要解决的“写写”、“读写”等并发冲突
单机系统使用加锁等方式进行并发控制,中心化协议也可使用。缺点是过度依赖中心节点。
这是一种经常使用的中心化副本控制协议,有且仅有一个节点做为primary副本。有4个问题须要解决:
(1) 数据更新基本流程
由primary协调完成更新
外部节点将更新操做发给primary节点
primary节点进行并发控制并肯定并发更新操做的前后顺序
primary节点将更新操做发送给secondary节点
primary根据secondary节点的完成状况决定更新是否成功,而后将结果返回外部节点
其中第4步,有些系统如GFS使用接力的方式同步数据,primary -> secondary1 ->secondary2,避免primary的带宽称为瓶颈。
(2) 数据读取方式
方法一:因为数据更新流程都是由primary控制的,primary副本上数据必定最新。因此永远只读primary副本的数据可以实现强一致性。为了不机器浪费,可使用以前讨论的方法,将数据分段,将数据段做为副本单位。达到每台机器都有primary副本的目的。
方法二:由primary控制secondary的可用性。当primary更新某个secondary不成功时,将其标记为不可用。不可用的secondary副本继续尝试与primary同步数据,直到成功同步后转为可用状态。
方法三:Quorum机制。后续讨论。
(3) primary副本的肯定与切换
如何肯定primary副本?primary副本所在机器异常时,如何切换副本?
一般在primary-secondary分布式系统中,哪一个副本为primary这一信息属于元信息,由专门元数据服务器维护。执行更新操做时,首先查询元数据服务器获取副本的primary信息,进一步执行数据更新流程。
切换副本难点有两个方面:如何肯定节点状态以发现原primary节点出现异常(Lease机制)。切换primary后不能影响一致性(Quorum机制)。
(4) 数据同步
当发生secondary与primary不一致的状况,须要secondary向primary进行同步(reconcile)。
不一致的缘由有3种:
网络分化等异常致使secondary落后于primary
经常使用的方式是回放primary上的操做日志(redo日志),追上primary的更新进度
某些协议下secondary是脏数据
丢弃转到第三种状况;或者设计基于undo日志的方式
secondary是一个新增副本彻底没有数据
直接copy primary的数据,但要求同时primary能继续提供更新服务,这就要求primary副本支持快照(snapshot)功能。即对某一刻的副本数据造成快照,而后copy快照,再使用回放日志的方式追赶快照后的更新操做。
去中心化副本控制协议没有中心节点,节点之间经过平等协商达到一致。工程中惟一能应用在强一致性要求下的是paxos协议。后续介绍。
(1) 需求:分布式系统中有一个节点A存储维护系统的元数据,系统中其余节点经过访问A读取修改元数据,致使A的性能成为系统瓶颈。为此,设计一种元数据cache,在各个节点上cache元数据信息,使得:
减小对A的访问,提升性能
各个节点cache数据始终与A一致
最大可能处理节点down机、网络中断等异常
(2) 解决方案原理:
A向各个节点发送数据的同时向节点颁发一个lease,每一个lease具备一个有效期,一般是一个明确的时间点。一旦真实时间超过次时间点则lease过时
在lease有效期内,A保证不会修改对应数据的值。
A在修改数据时,首先阻塞全部新的读请求,等待以前为该数据发出的全部lease过时,而后修改数据的值
(3) 客户端节点读取元数据的流程
if (元数据处于本地cache && lease处于有效期内) { 直接返回cache中的元数据; } else { Result = 向A请求读取元数据信息; if (Result.Status == SUCCESS) { WriteToCache(Result.data, Result.lease); } else if (Result.Status == FAIL || Result.Status == TIMEOUT) { retry() or exit(); } }
(4) 客户端节点修改元数据的流程
节点向A发起修改元数据的请求
A收到修改请求后阻塞全部新的读数据请求,即接受读请求但不返回数据
A等待全部与该元数据相关的lease超时
A修改元数据并向节点返回修改为功
(5) 优化点
A修改元数据时要阻塞全部新的读请求
这是为了防止发出新的lease而引发不断有新的cache节点持有lease,造成活锁。优化的手段是:一旦A进入修改流程,不是盲目屏蔽读请求,而是对读请求只返回数据不返回lease。形成cache节点只能读取数据,不能cache数据。进一步的优化是返回当前已发出的lease的最大值。这样一样能避免活锁问题。
A在修改元数据时须要等待全部lease过时
优化手段是:A主动通知各个持有lease的节点,放弃lease并清除cache中相关数据。若是收到cache节点的reply,确认协商经过,则能够提早完成修改动做;若是中间失败或超时,则进入正常等待lease过时的流程,不会影响协议正确性。
(1) lease定义
lease是由颁发者授予的再某一有效期内的承诺。颁发者一旦发出lease,不管接收方是否收到,不管后续接收方处于何种状态,只要lease不过时则颁发者必定严守承诺;另外一方面,接受者在lease的有效期内可使用颁发者的承诺,一旦lease过时则必定不能继续使用。
(2) lease的解读
因为lease仅仅是一种承诺,具体的承诺内容能够很是宽泛,能够是上节中数据的正确性,也能够是某种权限。例如并发控制中同一时刻只给某一个节点颁发lease,只有持有lease才能修改数据;例如primary-secondary中持有lease的节点具备primary身份等等
(3) lease的高可用性
有效期的引入,很是好的解决了网络异常问题。因为lease是肯定的时间点,因此能够简单重发;一旦受到lease以后,就再也不依赖网络通讯
(4) lease的时钟同步
工程上将颁发者有效期设置的比接受者打,大过期钟偏差,来避免对lease有效性产生影响。
在分 布式系统中肯定一个节点是否处于正常工做状态困难的问题。由可能存在网络分化,节点的状态没法经过网络通讯来肯定的。
例如: a b c 互为副本 a为主节点,q为监控节点。q经过ping来判断abc的状态,认为a不能工做。就将主节点切换成b。可是事实上仅仅是ping没收到,a本身认为本身没有问题,就出现了 a b都以为本身是主节点的“双主”问题。
解决方法是q在发送heartbeat时加上lease,表示确认了abc的状态,并容许节点在lease有效期内正常工做。q给primary节点一个特殊的lease,表示该节点做为primary工做。一旦q但愿切换primary,只须要等待以前primary的lease过时,避免出现双主问题。
工程上通常选择10秒钟
对于某次更新操做Wi,只有在全部N个副本上都更新成功,才认为是一次“成功提交的更新操做”,对应的数据为“成功提交的数据”,数据版本为Vi。
缺点:
频繁读写版本号容易成为瓶颈
N-1个副本成功的状况下,仍然被认为更新失败
WARO最大程度加强读服务可用性,最大程度牺牲写服务的可用性。将读写可用性折中,就会获得Quorum机制:
Quorum机制下,某次更新Wi一旦在全部N个副本中的W个副本上成功,就称为“成功提交的更新操做”,对应的数据为“成功提交的数据”。令R > N - W,最多须要读取R个副本则必定能读到Wi更新后的数据Vi。
因而可知WARO是Quorum中W = N时的一个特例。
Quorum的定义基于两个假设:
读取者知道当前已提交的版本号
每次都是一次成功的提交
如今取消这两个假设,分析下面这个实际问题:
N = 5, W = 3, R = 3,某一次的副本状态为(V2 V2 V2 V1 V1)。理论上读取任何3个副本必定能读到最新的数据V2,可是仅读3个副本却没法肯定读到最新的数据。
例如读到的是(V2 V1 V1),由于当副本状态为(V2 V1 V1 V1 V1)时也会读到(V2 V1 V1),而此时V2版本的数据是一次失败的提交。所以只读3个没法肯定最新有效的版本是V2仍是V1。
解决方案:
限制提交的更新操做必须严格递增,只有前一个更新操做成功后才能够提交下一个。保证成功的版本号连续
读取R个副本,对其中版本号最高的数据:若已存在W个,则该数据为最新结果;不然假设为X个,则继续读取其余副本直到成功读取W个该版本的副本。若是没法找到W个,则第二大的版本号为最新成功提交的版本。
所以单纯使用Quorum机制时,最多须要读取R + (W - R - 1) = N个副本才能肯定最新成功提交的版本。实际工程中通常使用quorum与primary-secondary结合的手段,避免须要读取N个副本。
在primary-secondary协议中引入quorum机制:即primary成功更新W个副本后向用户返回成功
当primary异常时须要选择一个新的primary,以后secondary副本与primary同步数据
一般状况下切换primary由监控节点q完成,引入quorum以后,选择新的primary的方式与读取数据类似,即q读取R个副本,选择R个副本中版本号最高的副本做为新的primary。新primary与除去q选举出的副本外,其他的至少W个副本完成数据同步后,再做为新的primary。
蕴含的道理是:
R个副本中版本号最高的副本必定蕴含了最新的成功提交的数据。
虽然不能肯定版本号最高的数据 == 这个最新成功提交的数据,但新的primary当即完成了对W个副本的更新,从而使其变成了成功提交的数据
例如:N = 5 W = 3 R = 3的系统,某时刻副本状态(V2 V2 V1 V1 V1),此时V1是最新成功提交的数据,V2是处于中间状态未成功提交的数据。V2是做为脏数据扔掉,仍是做为新数据被同步,彻底取决于可否参与q主持的新primary的选举大会。若是q选择(V1 V1 V1),则在接下来的更新过程当中 V2会被当成脏数据扔掉;若是q选择(V2 V1 V1)则V2会将V1更新掉,成为最新成功的数据。
日志技术不是一种分布式系统技术,但分布式系统普遍使用日志技术作down机恢复。
数据库的日志分为 undo redo redo/undo no-redo/no-undo四种,这里不作过多介绍。
经过redo log恢复down机的缺点是须要扫描整个redolog,回放全部redo日志。解决这个问题的办法是引入checkpoint技术,简化模型下checkpoint就是在begin和end中间,将内存以某种数据组织方式dump到磁盘上。这样down机恢复时只须要从最后一个end向前找到最近一个begin,恢复中间的内存状态便可。
这种技术也叫作01目录,即有两个目录,活动目录和非活动目录,另外还有一个主记录,要么“记录目录0”,妖魔记录“使用目录1”,目录0和1记录了各个数据在日志文件中的位置。
因为这两个都与数据库事务有关,且两阶段提交协议在工程中使用价值不高,均略去不谈。
惟一在工程中有使用价值的去中心化副本节点控制协议。过于复杂,没看懂。
Consitency
Availiablity
Tolerance to the Partition of network
没法设计一种分布式协议,使得彻底具有CAP三个属性。永远只能介于三者之间折中。理论的意义是:不要妄想设计完美的分布式系统。
针对上面的技术我特地整理了一下,有不少技术不是靠几句话能讲清楚,因此干脆找朋友录制了一些视频,不少问题其实答案很简单,可是背后的思考和逻辑不简单,要作到知其然还要知其因此然。若是想学习Java工程化、高性能及分布式、深刻浅出。微服务、Spring,MyBatis,Netty源码分析的朋友能够加个人Java进阶群:744642380,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给你们。