Google全球级分布式数据库Spanner原理(2)

来源:mysqlops 作者:mysqlops
  全球级的分布式数据库 Google Spanner原理

上图是一个例子。对于一个典型的相册应用,需要存储其用户和相册。可以用上面的两个SQL来创建表。Spanner的表是层次化的,最顶层的表是directory table。其他的表创建的时候,可以用interleave in parent来什么层次关系。这样的结构,在实现的时候,Spanner可以将嵌套的数据放在一起,这样在分区的时候性能会提升很多。否则Spanner无法获知最重要的表之间的关系。

 

TrueTime
全球级的分布式数据库 Google Spanner原理

TrueTime API 是一个非常有创意的东西,可以同步全球的时间。上表就是TrueTime API。TT.now()可以获得一个绝对时间TTinterval,这个值和UnixTime是相同的,同时还能够得到一个误差e。TT.after(t)和TT.before(t)是基于TT.now()实现的。

那这个TrueTime API实现靠的是GFS和原子钟。之所以要用两种技术来处理,是因为导致这两个技术的失败的原因是不同的。GPS会有一个天线,电波干扰会导致其失灵。原子钟很稳定。当GPS失灵的时候,原子钟仍然能保证在相当长的时间内,不会出现偏差。

实际部署的时候。每个数据中心需要部署一些Master机器,其他机器上需要有一个slave进程来从Master同步。有的Master用GPS,有的Master用原子钟。这些Master物理上分布的比较远,怕出现物理上的干扰。比如如果放在一个机架上,机架被人碰倒了,就全宕了。另外原子钟不是并很贵。Master自己还会不断比对,新的时间信息还会和Master自身时钟的比对,会排除掉偏差比较大的,并获得一个保守的结果。最终GPS master提供时间精确度很高,误差接近于0。

每个Slave后台进程会每个30秒从若干个Master更新自己的时钟。为了降低误差,使用Marzullo算法。每个slave还会计算出自己的误差。这里的误差包括的通信的延迟,机器的负载。如果不能访问Master,误差就会越走越大,知道重新可以访问。

 

Google Spanner并发控制

Spanner使用TrueTime来控制并发,实现外部一致性。支持以下几种事务。

·        读写事务

·        只读事务

·        快照读,客户端提供时间戳

·        快照读,客户端提供时间范围

例如一个读写事务发生在时间t,那么在全世界任何一个地方,指定t快照读都可以读到写入的值。
全球级的分布式数据库 Google Spanner原理

上表是Spanner现在支持的事务。单独的写操作都被实现为读写事务 ; 单独的非快照被实现为只读事务。事务总有失败的时候,如果失败,对于这两种操作会自己重试,无需应用自己实现重试循环。

时间戳的设计大大提高了只读事务的性能。事务开始的时候,要声明这个事务里没有写操作,只读事务可不是一个简单的没有写操作的读写事务。它会用一个系统时间戳去读,所以对于同时的其他的写操作是没有Block的。而且只读事务可以在任意一台已经更新过的replica上面读。

对于快照读操作,可以读取以前的数据,需要客户端指定一个时间戳或者一个时间范围。Spanner会找到一个已经充分更新好的replica上读取。

还有一个有趣的特性的是,对于只读事务,如果执行到一半,该replica出现了错误。客户端没有必要在本地缓存刚刚读过的时间,因为是根据时间戳读取的。只要再用刚刚的时间戳读取,就可以获得一样的结果。

 

读写事务

正如BigTable一样,Spanner的事务是会将所有的写操作先缓存起来,在Commit的时候一次提交。这样的话,就读不出在同一个事务中写的数据了。不过这没有关系,因为Spanner的数据都是有版本的。

在读写事务中使用wound-wait算法来避免死锁。当客户端发起一个读写事务的时候,首先是读操作,他先找到相关数据的leader replica,然后加上读锁,读取最近的数据。在客户端事务存活的时候会不断的向leader发心跳,防止超时。当客户端完成了所有的读操作,并且缓存了所有的写操作,就开始了两阶段提交。客户端闲置一个coordinator group,并给每一个leader发送coordinator的id和缓存的写数据。

leader首先会上一个写锁,他要找一个比现有事务晚的时间戳。通过Paxos记录。每一个相关的都要给coordinator发送他自己准备的那个时间戳。

Coordinatorleader一开始也会上个写锁,当大家发送时间戳给他之后,他就选择一个提交时间戳。这个提交的时间戳,必须比刚刚的所有时间戳晚,而且还要比TT.now()+误差时间 还有晚。这个Coordinator将这个信息记录到Paxos。

在让replica写入数据生效之前,coordinator还有再等一会。需要等两倍时间误差。这段时间也刚好让Paxos来同步。因为等待之后,在任意机器上发起的下一个事务的开始时间,都比如不会比这个事务的结束时间早了。然后coordinator将提交时间戳发送给客户端还有其他的replica。他们记录日志,写入生效,释放锁。

 

只读事务

对于只读事务,Spanner首先要指定一个读事务时间戳。还需要了解在这个读操作中,需要访问的所有的读的Key。Spanner可以自动确定Key的范围。

如果Key的范围在一个Paxos group内。客户端可以发起一个只读请求给group leader。leader选一个时间戳,这个时间戳要比上一个事务的结束时间要大。然后读取相应的数据。这个事务可以满足外部一致性,读出的结果是最后一次写的结果,并且不会有不一致的数据。

如果Key的范围在多个Paxos group内,就相对复杂一些。其中一个比较复杂的例子是,可以遍历所有的group leaders,寻找最近的事务发生的时间,并读取。客户端只要时间戳在TT.now().latest之后就可以满足要求了。

 

最后的话

本文介绍了GoogleSpanner的背景,设计和并发控制。希望不久的将来,会有开源产品出现。


时间:2012-09-20 13:10 来源:mysqlops 作者:mysqlops 原文链接

好文,顶一下
(1)
50%
文章真差,踩一下
(1)
50%
------分隔线----------------------------


把开源带在你的身边-精美linux小纪念品
无觅相关文章插件,快速提升流量