7.3.5 并发控制
Spanner使用TrueTime来控制并发,实现外部一致性,支持以下几种事务:
●读写事务
●只读事务
●快照读,客户端提供时间戳
●快照读,客户端提供时间范围
1.不考虑TrueTime
首先,不考虑TrueTime的影响,也就是说,假设TrueTime API获得的时间是精确的。如果事务读写的数据只属于同一个Paxos组,那么,每个读写事务的执行步骤如下:
1)获取系统的当前时间戳;
2)执行读写操作,并将第1步取得的时间戳作为事务的提交版本。
每个只读事务的执行步骤如下:
1)获取系统的当前时间戳,作为读事务的版本;
2)执行读取操作,返回客户端所有提交版本小于读事务版本的事务操作结果。
快照读和只读事务的区别在于:快照读将指定读事务的版本,而不是取系统的当前时间戳。
如果事务读写的数据涉及多个Paxos组,那么,对于读写事务,需要执行一次两阶段提交协议,执行步骤如下:
1)Prepare:客户端将数据发往多个Paxos组的主副本,同时,协调者主副本发起prepare协议,请求其他的参与者主副本锁住需要操作的数据。
2)Commit:协调者主副本发起Commit协议,要求每个参与者主副本执行提交操作并解除Prepare阶段锁定的数据。协调者主副本可以将它的当前时间戳作为该事务的提交版本,并发送给每个参与者主副本。
只读事务读取每个Paxos组中提交版本小于读事务版本的事务操作结果。需要注意的是,只读事务需要保证不会读到不完整的事务。假设有一个读写事务修改了两个Paxos组:Paxos组A和Paxos组B,Paxos组A上的修改已提交,Paxos组B上的修改还未提交。那么,只读事务会发现Paxos组B处于两阶段提交协议中的Prepare阶段,需要等待一会,直到Paxos组B上的修改生效后才能读到正确的数据。
2.考虑TrueTime
如果考虑TrueTime,并发控制变得复杂。这里的核心思想在于,只要事务T1的提交操作早于事务T2的开始操作,即使考虑TrueTime API的误差因素(-e到+e之间,e值平均为4ms),Spanner也能保证事务T1的提交版本小于事务T2的提交版本。Spanner使用了一种称为延迟提交(Commit Wait)的手段,即如果事务T1的提交版本为时间戳tcommit,那么,事务T1会在tcommit+e之后才能提交。另外,如果事务T2开始的绝对时间为tabs,那么事务T2的提交版本至少为tabs+e。这样,就保证了事务T1和T2之间严格的顺序,当然,这也意味着每个写事务的延时至少为2e。从这一点也可以看出,Spanner实现功能完备的全球数据库是付出了一定代价的,设计架构时不能盲目崇拜。