分布式事务实现-Spanner

  Spanner要知足的external consistency是指:后开始的事务必定能够看到先提交的事务的修改。全部事务的读写都加锁能够解决这个问题,缺点是性能较差。特别是对于一些workload中只读事务占比较大的系统来讲不可接受。为了让只读事务不加任何锁,须要引入多版本。在单机系统中,维护一个递增的时间戳做为版本号很好办。分布式系统中,机器和机器之间的时钟有偏差,而且偏差范围不肯定,带来的问题就是很难判断事件发生的先后关系。反应在Spanner中,就是很难给事务赋予一个时间戳做为版本号,以知足external consistency。在这样一个偏差范围不肯定的分布式系统时,一般,得到两个事件发生的前后关系主要经过在节点之间进行通讯分析其中的因果关系(casual relationship),经典算法包括Lamport时钟等算法。然而,Spanner采用不一样的思路,经过在数据中心配备原子钟和GPS接收器来解决这个偏差范围不肯定的问题,进而解决分布式事务时序这个问题。基于此,Spanner提供了TrueTime API,返回值实际为一个区间[t-ε,t+ε],ε为时间偏差,毫秒级,保证当前的真实时间位于这个区间。算法

  Spanner是一个支持分布式读写事务,只读事务的分布式存储系统,只读事务不加任何锁。和其余分布式存储系统同样,经过维护多副原本提升系统的可用性。一份数据的多个副本组成一个paxos group,经过paxos协议维护副本之间的一致性。对于涉及到跨机的分布式事务,涉及到的每一个paxos group中都会选出一个leader,来参与分布式事务的协调。这些个leader又会选出一个大leader,称为coordinator leader,做为两阶段提交的coordinator,记做coordinator leader。其余leader做为participant。数据库

  数据库事务系统的核心挑战之一是并发控制协议。Spanner的读写事务使用两阶段锁来处理。详细流程下图所示。并发

  如第一段所述,给事务赋予一个时间戳版本号是这样一个分布式存储系统的核心。下面先说如何肯定读写事务的版本号,再说只读事务。app

  前面已经说了两阶段提交过程当中两阶段锁的过程,这里就省略这些,只讨论两阶段提交过程当中如何肯定最后的读写事务的时间戳版本号。分布式

  读写事务ide

 

 

 

 

 

 

简要时序图以下:性能

  

 

图中,commit wait分为两段,第一阶段是论文中4.1.2节提到的Start,第二阶段是4.1.2节的Commit Wait google

只读事务spa

   调用TrueTime API,将右区间做为只读事务的版本号,记做Sread, 若是读事务涉及到的副本不够新,那么读会阻塞。每一个副本会维护一个时间戳Tsafe, 若是Sread <= Tsafe ,则这个副本够新。Tsafe取两个时间戳的更小值,第一个是副本所在的paxos group中已经apply到状态机的时间戳,第二个是paxos group中leader目前正在参与的尚未commit的分布式事务在该paxos group的最小的prepared timestamp - 1。.net

   还有一种方法,能够不用客户端等,方法是Sread经过客户端和全部涉及到的paxos group的leader进行协商,每一个leader返回他们的最后一次事务的commit时间戳给客户端,而后客户端取最小的版本号做为Sread便可。

下面说一下两阶段提交的错误处理。

  两阶段提交协议因为协调者和参与者的故障可能会有严重的可用性问题。Spanner的两阶段提交实现基于Paxos协议,每一个participant和coordinator自己产生的日志都会经过paxos协议复制到自身的paxos group中,从而解决可用性问题。一样以A,B,C三份数据为例,他们分别有三个副本,记做(A1,A2,A3),(B1,B2,B3),(C1,C2,C3),每组做为一个paxos group,内部经过paxos协议保证一致性。假设,A1,B1,C1分别为各自paxos group的leader,A1为coordinator leader。

  Prepare阶段:A1给B1和C1发送prepare消息后,假设B1挂了,A1等待超时,A1给C1发送rollback。B1后续回滚分为两种状况:1. B1在持久化prepare消息以前挂了,B1恢复后可自行回滚 2. 若是B1持久化prepare消息以后挂了,B1自身能够回放日志得知事务未决,主动联系(A1,A2,A3)。A1给B1,C1发送prepare消息以后,本身挂了,一样,A1经过回放日志能够得知。实际上,A1自己挂了以后,A2和A3经过选主协议立刻会选出一个新的leader,不至于影响到可用性。

   Commit阶段:A1给B1,C1发送commit消息,B1 commit成功,C1挂了,C1起来后,若是C1以前没有持久化commit消息,则A1主动要求C1继续commit。若是C1以前已经持久化了commit消息,则本身commit。若是C1因为某些缘由,始终commit不成功,则由上层业务进行回补操做。

 

参考资料

Spanner: Google’s Globally-Distributed Database

presentation:Spanner: Google’s Globally-Distributed Database

相关文章
相关标签/搜索