分布式学习之一:事务

 

 

事务处理

让咱们用最经典的 Use Case:“A账号向B账号汇钱”来讲明一下,熟悉RDBMS事务的都知道从账号A到账号B须要6个操做:node

  1. 从A账号中把余额读出来;
  2. 对A账号作减法操做;
  3. 把结果写回A账号中;
  4. 从B账号中把余额读出来;
  5. 对B账号作加法操做;
  6. 把结果写回B账号中;

为了数据的一致性,这6件事,要么都成功作完,要么都不成功,并且这个操做的过程当中,对A、B账号的其它访问必需锁死,所谓锁死就是要排除其它的读写操做,否则会有脏数据的问题,这就是事务mysql

若是A账号和B账号的数据不在同一台服务器上怎么办?咱们须要一个跨机器的事务处理。也就是说,若是A的扣钱成功了,但B的加钱不成功,咱们还要把A的操做给回滚回去。这在跨机器的状况下,就变得比较复杂了。算法

若是不考虑性能的话,保证事务并不困难,系统慢一点就好了;除了考虑性能外,咱们还要考虑可用性,也就是说,一台机器没了,数据不丢失,服务可由别的机器继续提供。 因而,咱们须要重点考虑下面的这么几个状况:sql

  • 容灾:数据不丢、节点的Failover;
  • 数据的一致性:事务处理;
  • 性能:吞吐量 、 响应时间

 

要解决数据不丢,只能经过数据冗余的方法,就算是数据分区,每一个区也须要进行数据冗余处理。这就是数据副本:当出现某个节点的数据丢失时能够从副本读到,数据副本是分布式系统解决数据丢失异常的惟一手段。因此在数据冗余状况下考虑数据的一致性和性能的问题:数据库

1)要想让数据有高可用性,就得写多份数据。服务器

2)写多份的问题会致使数据一致性的问题。网络

3)数据一致性的问题又会引起性能问题并发

这就是软件开发,按下了葫芦起了瓢。异步

 

数据一致性能够简单分为三类:分布式

  1. 弱一致性(weak),当你写入一个新值后,读操做在数据副本上可能读出来,也可能读不出来。
  2. 强一致性(strong),新的数据一旦写入,在任意副本任意时刻都能读到新值。
  3. 最终一致性(eventually),当你写入一个新值后,有可能读不出来,但在某个时间窗口以后保证最终能读出来。

 

 


 

2PC协议

在分布式系统中,每一个节点虽然能够知晓本身的操做时成功或者失败,却没法知道其余节点的操做的成功或失败。当一个事务跨越多个节点时,引入一个做为协调者(coordinator)的组件来统一掌控全部节点(称做参与者)的操做结果,并最终指示这些节点是否要把操做结果进行真正的提交(好比将更新后的数据写入磁盘等等)。

 

两阶段提交(two phase commit, 2PC)的算法以下:

第一阶段(vote)

  1. 协调者问全部的参与者,是否能够执行提交操做;
  2. 各个参与者开始事务执行的准备工做:如:为资源上锁,预留资源,写undo/redo log;
  3. 参与者响应协调者,若是事务的准备工做成功,则回应“能够提交”,不然回应“拒绝提交”;

第二阶段(commit)

  1. 若是全部的参与者都回应“能够提交”,那么,协调者向全部的参与者发送“正式提交”的命令。参与者完成正式提交,并释放全部资源,而后回应“完成”,协调者收集各结点的“完成”回应后结束这个Global Transaction;
  2. 若是有一个参与者回应“拒绝提交”,那么,协调者向全部的参与者发送“回滚操做”,并释放全部资源,而后回应“回滚完成”,协调者收集各结点的“回滚”回应后,取消这个Global Transaction;

 

2PC实现了强一致性,其最大缺点就是它经过阻塞完成,会极大影响性能;另外一个问题则在timeout上:

  1. 第一阶段中,若是参与者没有收到询问请求,或是参与者的回应没有到达协调者。那么,须要协调者作超时处理,一旦超时,能够看成失败,也能够重试。
  2. 第二阶段中,正式提交发出后,若是有的参与者没有收到,或是参与者提交/回滚后的确认信息没有返回,一旦参与者的回应超时,要么重试,要么把那个参与者标记为问题结点剔除整个集群,这样能够保证服务结点都是数据一致性的。
  3. 第二阶段中,若是参与者收不到协调者的commit/fallback指令,参与者将处于“状态未知”阶段,参与者彻底不知道要怎么办,好比:若是全部的参与者完成第一阶段的回复后(可能所有yes,可能所有no,可能部分yes部分no),若是协调者在这个时候挂掉了。那么全部的结点彻底不知道怎么办(问别的参与者都不行),这个状态会block整个事务,为此,引入了3PC的概念。

 

3PC协议

3PC是把二段提交的第一个段break成了两段:询问,而后再锁资源。最后真正提交。其核心理念是:在询问的时候并不锁定资源,除非全部人都赞成了,才开始锁资源

 

理论上来讲,若是第一阶段全部的结点返回成功,那么有理由相信成功提交的几率很大。这样一来,能够下降参与者Cohorts的状态未知的几率。也就是说,一旦参与者收到了PreCommit,意味他知道你们其实都赞成修改了。这一点很重要。下面咱们来看一下3PC的状态迁移图:(注意图中的虚线,那些F,T是Failuer或Timeout,其中的:状态含义是 q – Query,a – Abort,w – Wait,p – PreCommit,c – Commit)

 

从上图的状态变化图咱们能够从虚线(那些F,T是Failuer或Timeout)看到——若是结点处在P状态(PreCommit)的时候发生了F/T的问题,三段提交比两段提交的好处是,三段提交能够继续直接把状态变成C状态(Commit),而两段提交则不知所措

 

 

paxos算法

Paxos是一种民主选举的算法,目的是让整个集群对某个值的变动达成一致,大多数节点的决定会成个整个集群的决定。任何一个点均可以提出要修改某个数据的提案,是否经过这个提案取决于这个集群中是否有超过半数的结点赞成(须要集群中的节点是单数)。

这个算法有两个阶段(假设这个有三个结点:A,B,C):

第一阶段:Prepare阶段

A把申请修改的请求Prepare Request发给全部的结点A,B,C。注意,Paxos算法会有一个Sequence Number(提案号,这个数不断递增,并且是惟一的,也就是说A和B不可能有相同的提案号),这个提案号会和修改请求一同发出,任何结点在“Prepare阶段”时都会拒绝其值小于当前提案号的请求。因此,结点A在向全部结点申请修改请求的时候,须要带一个提案号,越新的提案,这个提案号就越是是最大的。

若是接收结点收到的提案号n大于其它结点发过来的提案号,这个结点会回应Yes(本结点上最新的被批准提案号),并保证不接收其它<n的提案。这样一来,结点上在Prepare阶段里老是会对最新的提案作承诺。

优化:在上述 prepare 过程当中,若是任何一个结点发现存在一个更高编号的提案,则须要通知 提案人,提醒其中断此次提案。

第二阶段:Accept阶段

若是提案者A收到了超过半数的结点返回的Yes,而后他就会向全部的结点发布Accept Request(一样,须要带上提案号n),若是没有超过半数的话,那就返回失败。

当结点们收到了Accept Request后,若是对于接收的结点来讲,n是最大的了,那么,它就会修改这个值,若是发现本身有一个更大的提案号,那么,结点就会拒绝修改。

 

咱们能够看以,这彷佛就是一个“两段提交”的优化。其实,2PC/3PC都是分布式一致性算法的残次版本,Google Chubby的做者Mike Burrows说过这个世界上只有一种一致性算法,那就是Paxos,其它的算法都是残次品。

 

 


 

CAP理论

  • 一致性(Consistency),一致性指“all nodes see the same data at the same time”,即更新操做成功并返回客户端完成后,全部节点在同一时间的数据彻底一致;
  • 可用性(Availability),可用性指“Reads and writes always succeed”,即服务一直可用,并且是正常响应时间;
  • 分区容错性(Partition tolerance),分区容错性指“the system continues to operate despite arbitrary message loss or failure of part of the system”,即分布式系统在遇到某节点或网络分区故障的时候,仍然可以对外提供知足一致性和可用性的服务;

 

CAP理论为:一个分布式系统最多只能同时知足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。

对于多数大型互联网应用的场景,主机众多、部署分散,并且如今的集群规模愈来愈大,因此节点故障、网络故障是常态,并且要保证服务可用性达到N个9,即保证P和A,舍弃C(退而求其次保证最终一致性)。虽然某些地方会影响客户体验,但没达到形成用户流程的严重程度。

但对于银行等金融机构,C必须保证。网络发生故障宁肯中止服务,这是保证CA,舍弃P。

 

 

ACID理论

数据库事务(Transaction)是指做为单个逻辑工做单元执行的一系列操做,要么彻底地执行,要么彻底地不执行。

一方面,当多个应用程序并发访问数据库时,事务能够在应用程序间提供一个隔离方法,防止互相干扰。另外一方面,事务为数据库操做序列提供了一个从失败恢复正常的方法。

事物有下面4个特性:

  • 原子性(Atomicity),事务的原子性是指事务中的操做不可拆分,只容许所有执行或者所有不执行;
  • 一致性(Consistency),事务的一致性是指事务的执行不能破坏数据库的一致性,一致性也称为完整性。一个事务在执行后,数据库必须从一个一致性状态转变为另外一个一致性状态;
  • 隔离型(Isolation),事务的隔离型是指并发的事务相互隔离,不能互相干扰;
  • 持久性(Durability),事务的持久性是指事务一旦提交,对数据的状态变动应该被永久保存;

 

实际工做中事务几乎都是并发的,彻底作到互相之间不干扰会严重牺牲性能,为了平衡隔离型和性能,SQL92规范定义了四个事务隔离级别:

  1. 读未提交(Read Uncommitted),另外一个事务修改了数据,但还没有提交,而本事务中的SELECT会读到这些未被提交的数据(脏读)。脏读是指另外一个事务修改了数据,但还没有提交,而本事务中的SELECT会读到这些未被提交的数据。
  2. 读已提交(Read Committed),本事务读取到的是最新的数据(其余事务提交后的)。问题是,在同一个事务里,先后两次相同的SELECT会读到不一样的结果(不可重复读)。不可重复读是指同一个事务执行过程当中,另一个事务提交了新数据,所以本事务前后两次读到的数据结果会不一致。
  3. 可重复读(Repeatable Read),在同一个事务里,SELECT的结果是事务开始时间点的状态,一样的SELECT操做读到的结果会是一致的。可是,会有幻读现象。不可重复读保证了同一个事务里,查询的结果都是事务开始时的状态(一致性)。可是,若是另外一个事务同时提交了新数据,本事务再更新时,就会发现了这些新数据,貌似以前读到的数据是幻觉,这就是幻读。
  4. 串行化(Serializable),全部事务只能一个接一个串行执行,不能并发。

事务隔离级别越高,越能保证数据的一致性,但对并发性能影响越大,一致性和高性能必须有所取舍或折中。

通常状况下,多数应用程序能够选择将数据库的隔离级别设置为读已提交,这样能够避免脏读,也能够获得不错的并发性能。尽管这个隔离级别会致使不可重复度、幻读,但这种个别场合应用程序能够经过主动加锁进行并发控制。

 

 

BASE理论

BASE理论是对CAP理论的延伸,核心思想是即便没法作到强一致性(Strong Consistency,CAP的一致性就是强一致性),但应用能够采用适合的方式达到最终一致性(Eventual Consitency)。

  • 基本可用(Basically Available),基本可用是指分布式系统在出现故障的时候,容许损失部分可用性,即保证核心可用;
  • 软状态( Soft State),软状态是指容许系统存在中间状态,而该中间状态不会影响系统总体可用性。分布式存储中通常一份数据至少会有三个副本,容许不一样节点间副本同步的延时就是软状态的体现。mysql replication的异步复制也是一种体现;
  • 最终一致性( Eventual Consistency),最终一致性是指系统中的全部数据副本通过必定时间后,最终可以达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊状况。

 

BASE支持的是大型分布式系统,提出经过牺牲强一致性得到高可用性。