分布式系统信息一致性问题与方案分析

用户头像
superman
关注
发布于: 2020 年 07 月 17 日

1: 问题分析与定义

一致性研究的是存储系统读写过程中信息的一致与冲突问题,分布式系统一致性是其中的一个场

景,限制在分布式环境下。



1.1 存储系统

角色

后端存储:负责保存信息,提供信息的读写功能

写客户端:将信息写入后端存储。

读客户端:从后端存储读取数据。

过程

读写过程可以成功或失败。成功失败的判断可以是后端反馈,也可以是客户端根据反馈结果判断。

 

1.2 信息的粒度

如果某个信息每次读写都是整体进行,这个信息就具有原子性,否则就不具有原子性。后面分别称为原子信息,非原子信息

数据库一个事务更新的是多个记录,每次更新的粒度不同,读取的粒度也不同。因此数据库事务内的信息就不具有原子性。

kv存储的数据,单个key的数据,每次读写粒度都是相同的,单个key的数据就具有原子性属于原子信息。如果要把多个key的更新放到一个事务里,每次更新的key组合不同,读的时候也不是按这个粒度进行的,多个key的数据组合就不具有原子性,属于非原子信息。

1.3 信息一致性

1.3.1 一致性场景限制

一致性场景限制

1:信息会变更

不变更就不会出现不一致

比如消息队列,但只追加,不更改。不属于一致性研究范围

 2:读客户端并发读

读客户端是多个,并且是并发读。



分布式系统一致性场景限制为

一致性场景限制+信息在多个节点存储。

1:信息会变更

2:读客户端并发读

3:信息存储在多个位置,读取的时候是从多个位置读的。

      数据分布存储,客户端分散读。

      按信息的原子性与物理分步分为两种

      原子信息,多个节点存放

             各个节点上存放的是相同的信息,都是完整的

              比如典型的分布式存储(分布式缓存redis,zookeeper,etcd)。

       非原子信息多个节点分别存放不同部分。

              按业务特点分为相关的几个部分分别在不同节点存放,

分布式服务架构下的一次请求触发多个服务,更新多个不同类型数据。

      注意必须是分散读,否则一致性问题解决就简单或就不存在了。

4:后端存储接收写请求的节点不要求多个

      可以单个节点处理,可以多节点处理。



1.3.2 一致性定义

一致性是指多个读客户端从后端存储中读取信息时,在同一时刻读取到的信息相同,是最新写入成功的信息。一致性=相同+最新两个特性。

相同

相同指多个客户端并发读到的信息相同,不考虑节点存放的是否完全一致。

最新

读到最近写成功的数据,不同于节点上的最新存储数据(可能是中间态)。

 

1.3.3 单节点存储系统一致性

单节点方便保证顺序性。

原子信息

节点只要进行顺序更新即可保住一致性。

单节点kv系统。redis,memcached.本地文件系统

非原子信息

节点要通过策略保证整体的更改是原子的,并发更改是顺序的。

典型的就是关系数据库,通过事务满足的ACID特性。

 

1.3.4 分布式存储系统一致性

分布式存储下,信息是多节点存放,多个节点写总有先后,各个节点的信息客观上就存在不一致的时刻。

1.3.4.1 强一致性

客户端分散的读单个节点,无法满足任意时刻都相同。

将多个节点信息包装为一个整体,每次读都是读多个节点,读到的节点间信息如果一致或可以决策出来一致的信息(冲突解决)就认为成功,否则就认为失败。可以满足任意时刻读到一致性数据。

完全满足一致性的定义就称为强一致性

 

1.3.4.2 最终一致性

分布式下满足强一致性有很高的代价(牺牲可用性,性能等),因此很多场景下为了可用性会放低一致性的要求(CAP理论),平衡场景需求。

允许客户端读取到中间态的数据,但最终数据会一致,实现最终一致性,即BASE模式。很多NOSQL实现满足 BASE,满足最终一致性。实际中不一致的时间不能太长,越短越好。

多长时间达到最终一致性要看具体业务要求,时间长度要满足业务要求。否则就不能用。

1.3.4.3 写后读到新数据

最终一致性中有两种方法情况:

1:写客户端明确写成功后,客户端读一定可以读到新数据。

2:写客户端明确写成功后,客户端读不能保证最新,但可以可以保证不会读到未提交成功的数据(保证读已到的是已提交数据)。如果客户端记录上次读取的版本,按版本逐个读取可以保读的顺序一致性。

在有些场景下客户端再次读取是因为写成功触发的,因此如果写客户端获知成功了,读取能得到最新数据,也满足要求。如果这种场景同时写不是高并发的,类似满足强一致性。

满足写后读到新数据



2:分布式一致性的模型与协议

分布式一致性协议是特定信息分布模型下实现一致性的方法。

 

可从多个角度去审视各种实现。以下从以下个角度综合看

信息源头:信息分布模型,原子信息多节点分布,非原子信息多节点分布。

更新方法:写节点数,单节点负责,多节点负责

读的方法:单节点读,多节点读

单节点读在分布式存储下是不能保证读完全一致性(会出现读过期或读未提交),后续所有方案中只要是单读就会有这个问题

一致性要求:强一致性,最终一致性,写后读一致性。



2.1原子信息单节点写

     典型的主从模式。比如mysql主从,redis主从。

     单节点写原子信息就是一种顺序写,没有数据并发写冲突问题,有数据延迟问题。

如果主节点可以在失效后重新选举,要解决主节点选举及主节点挂掉后防止数据回滚(丢失)。

     拷贝方式:

             同步拷贝:

                       从节点全部拷贝成功后再反馈成功,否则失败回滚。

                       从节点部分拷贝成功,反馈成功。

             异步拷贝:写节点成功就反馈成功

   强一致性:

            同步拷贝(全部或大部分)+多读

    写完读到新数据

同步拷贝(全部)+单读

保读已提交

同步拷贝(大部分)+单读

   最终一致性

             异步拷贝+单读

一主多从模式,自动选举,确认主节点,写信息时,主节点写,并且多数从节点同意即反馈成功,并可保证一致性。Paxos ,ZAB,Raft 等共识算法与通过这些算法实现一致性的zookeeper,etcd 等均满足的是读已提交。特征如下。

1:主节点同步复制到大部分节点返回成功

2:主节点挂掉|到期重新选主

3:反馈写成功的即便主节点挂掉数据也不会回滚(丢失)

4:保读的顺序一致性,不保强一致性。

2.2 非原子信息单点协调(专制)

    这种情况不存在单写,但可以单个节点负责协调。

分布式事务方案,典型的XA规范与两阶段提交

2.2.1 两阶段提交方案

角色

TM:全局协调者(事务管理器),

RM:资源管理器

两个阶段

阶段1:准备阶段

         目的是RM对资源进行锁定|预处理。

         如果准备节点失败就全部放弃执行。

阶段2:提交阶段。

         逐个提交

         如果出现故障,让其他节点进行回滚。

         释放资源锁

特点

RM对资源的锁定,可对影响的资源整体做排它锁定,也可以对影响的资源按更新特点进行部分预处理(比如账户冻结部分资金,而不是冻结账户的支付功能)。

如果是锁定整体,释放资源时有先后不能保证强一致,如果引入中间态也不能保证,不过对读来说对这部分不一致的预期已经有了,反而可能更容易接受。

即预处理模式是对资源引入了一种中间状态,实现在全部提交前读一致性(不强一致性,但符合预期)。

异常发生时的问题与处理

在提交阶段,部分提交成功后,资源在这部分RM中就已经释放了,部分资RM出现分区故障可能较长时间不一致性或无法恢复一致性,需要人工介入或程序检查。

引入中间状态要求业务进行相应的修改,并提供进行提交的回滚的接口。

避免提交中节点故障需要记录日志方便重试与回滚。

 

分布式事务XA协议属于在DB层锁定

TCC属于业务层实现的两阶段,并引入中间状态,业务层要引入预处理(添加中间状态实现),业务侵入大,也更灵活。

2.2.2 升级两阶段提交-全局锁

上面的两阶段提交做不到在资源层面强一致性。对两阶段提交进行增强引入全局锁可实现。

建立资源全局锁

阶段1:准备阶段

         目的是RM对资源进行锁定(本地锁)。

         如果准备节点失败就全部放弃执行。

申请资源全局锁

阶段2:提交阶段。

         逐个提交

         如果出现故障,让其他节点进行回滚。

         释放资源锁(本地锁)

释放全局锁

释放资源全局锁

读与写如果都进行全局锁申请,可以做到强一致性。执行性能可用性上代价较高。

Seata AT模式采用是全局锁实现强一致性。

2.3 非原子信息多节点负责(授权+监督)

         数据不是原子,同时支持多个节点负责写,要保证强一致性是不可能的。只满足最终一致性。

         实现方案:

         联邦制

         有统一监督者了解各个节点执行情况执,监督者负责协调冲突

        各节点执行过程互不影响,单独行事,但有监督者观察各节点执行情况,在出现问题时协调冲突

         比如分布式服务下,入口服务同步调用其他服务,但并不采用分布式事务。在出现错误时入口服务知道调用结果,如果调用某个服务失败,也可以对之前成功的服务再次调用回滚接口(或其他手段补偿)。

Saga模式可以认为就是一直联邦制



       分布式刚性事务的事务管理器也是一个监督者的角色(命令者)。权力更大,对读写请求做了更多的管控。协调子事务步调,甚至读操作也控制,所以归为单节点协调。

而多节点负责的监督角色,对各个写节点管控更弱,子节点本地事务不相互等待,提交失败监督节点可获得。补偿实现子节点灵活定制。



        邦联制:没有统一监督者,大家平等,都按照某种约定执行

        各节点按某种共同的规范各自行事。出现问题时按规范协商解决。

        比如事件驱动的服务,触发事件后多个服务都监控这个事件,分别执行。失败后补偿。

        补偿手段可以是有一个专门服务去核对纠正,也可以是各个节点主动再次去协调后处理(失败者重试,成功者再次去核对等手段实现最终一致性)。

分布式消息事务可以认为是一种邦联制,或理解为有沟通无管理。



        特定场景可以实现强一致性:(分开存放,但不是严格分散读,而是关联读,顺序读)

        读的时候只根据最后成功的部分读,最后部分成功客户端才会继续读其他部分。比如一个业务先存储文件,再存储数据库,文件索引记录在数据库中,数据库事务最后提交。客户端查的时候是先从数据库查到数据,然后才去文件系统查。如果文件存储成功,数据库事务失败,即便不做处理,系统中只是浪费部分存储,不影响客户端的一致性。

        --这种情况严格意义上不属于分散读取,算关联读。

2.4 原子信息多节点写

        写节点与信息是否绑定

        绑定:整体没有主节点,是多个节点写,但具体信息都有专门的主节点。

        不绑定:信息无归属主节点,多个节点可写。

2.4.1 信息绑定节点-分区

        信息与写节点绑定的实现方案类似单节点写,只是在信息与归属节点做了一层路由,即数据分区。hbase,redis集群的cluster模式属于这种模式。为了多读或高可用主下面再挂多个从。主从间同步或异步复制。这种多节点写本质还是单节点写

2.4.2 信息不绑定的方案

 

         如何写到多个节点有两种方式。

         客户端写到多个节点(或全部节点)。

         客户端写到后台单个节点,由后台节点复制到其他节点。

         如果写到部分节点也有一层数据路由算法。

         客户端多写与服务端多写原理类似只是实现是在客户端还是服务端。

         都要解决并发写导致冲突问题:要么通过数据粒度的锁,要么根据写数据的级(比如时间戳等方式确认)。复杂性反而比较大,所以最多的还是根据数据的key计算出应该负责的节点,然后写多份,即信息与节点绑定的方式。



CAP是对分布式一致性解决方案特点的一种观察角度,归纳的出的一种规律。

对CAP的总结参考下篇 CAP理论理解

参考:

https://baijiahao.baidu.com/s?id=1634759073848664238&wfr=spider&for=pc

https://www.cnblogs.com/bluemiaomiao/p/11216380.html

https://www.jianshu.com/p/71b2729d3004

https://www.cnblogs.com/jajian/p/10014145.html#scroller-0

https://seata.io/zh-cn/docs/overview/what-is-seata.html



发布于: 2020 年 07 月 17 日 阅读数: 28
用户头像

superman

关注

还未添加个人签名 2018.07.20 加入

还未添加个人简介

评论

发布
暂无评论
分布式系统信息一致性问题与方案分析