Google Spanner和F1介绍

zz from: http://blog.sina.com.cn/s/blog_4a1f59bf01017xiv.htmlhtml

 

ZZ From http://qing.weibo.com/2294942122/88ca09aa3300221n.html
Google Spanner原理- 全球级的分布式数据库算法

标签:大数据云计算sql

Google Spanner简介数据库

Spanner 是Google的全球级的分布式数据库 (Globally-Distributed Database) 。Spanner的扩展性达到了使人咋舌的全球级,能够扩展到数百万的机器,数已百计的数据中心,上万亿的行。更给力的是,除了夸张的扩展性以外,他还能 同时经过同步复制和多版原本知足外部一致性,可用性也是很好的。冲破CAP的枷锁,在三者之间完美平衡。缓存

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

Spanner是个可扩展,多版本,全球分布式还支持同步复制的数据库。他是Google的第一个能够全球扩展而且支持外部一致 的事务。Spanner能作到这些,离不开一个用GPS和原子钟实现的时间API。这个API能将数据中心之间的时间同步精确到10ms之内。所以有几个 给力的功能:无锁读事务,原子schema修改,读历史数据无block。并发

EMC中国研究院实时紧盯业界动态,Google最近发布的一篇论文《Spanner: Google's Globally-Distributed Database》, 笔者很是感兴趣,对Spanner进行了一些调研,并在这里分享。因为Spanner并非开源产品,笔者的知识主要来源于Google的公开资料,经过现有公开资料仅仅只能窥得Spanner的沧海一粟,Spanner背后还依赖有大量Google的专有技术。负载均衡

下文主要是Spanner的背景,设计和并发控制。运维

 

Spanner背景分布式

要搞清楚Spanner原理,先得了解Spanner在Google的定位。oop

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

 

 

从上图能够看到。Spanner位于F1和GFS之间,承上启下。因此先提一提F1和GFS。

 

F1

和众多互联网公司同样,在早期Google大量使用了Mysql。Mysql是单机的,能够用Master-Slave来容错, 分区来扩展。可是须要大量的手工运维工做,有不少的限制。所以Google开发了一个可容错可扩展的RDBMS——F1。和通常的分布式数据库不一样,F1 对应RDMS应有的功能,绝不妥协。起初F1是基于Mysql的,不过会逐渐迁移到Spanner。

F1有以下特色:

·        7×24高可用。哪怕某一个数据中心中止运转,仍然可用。

·        能够同时提供强一致性和弱一致。

·        可扩展

·        支持SQL

·        事务提交延迟50-100ms,读延迟5-10ms,高吞吐

众所周知Google BigTable是重要的NoSql产品,提供很好的扩展性,开源世界有HBase与之对应。为何Google还须要F1,而不是都使用 BigTable呢?由于BigTable提供的最终一致性,一些须要事务级别的应用没法使用。同时BigTable仍是NoSql,而大量的应用场景需 要有关系模型。就像如今大量的互联网企业都使用Mysql而不肯意使用HBase,所以Google才有这个可扩展数据库的F1。而Spanner就是 F1的相当重要的底层存储技术。

 

Colossus(GFS II)

Colossus也是一个不得不提起的技术。他是第二代GFS,对应开源世界的新HDFS。GFS是著名的分布式文件系统。

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

 

 

初代GFS是为批处理设计的。对于大文件很友好,吞吐量很大,可是延迟较高。因此使用他的系统不得不对GFS作各类优化,才能获 得良好的性能。那为何Google没有考虑到这些问题,设计出更完美的GFS ?由于那个时候是2001年,Hadoop出生是在2007年。若是Hadoop是世界领先水平的话,GFS比世界领先水平还领先了6年。一样的 Spanner出生大概是2009年,如今咱们看到了论文,估计Spanner在Google已经很完善,同时Google内部已经有更先进的替代技术在 酝酿了。笔者预测,最先在2015年才会出现Spanner和F1的山寨开源产品。

Colossus是第二代GFS。Colossus是Google重要的基础设施,由于他能够知足主流应用对FS的要求。Colossus的重要改进有:

·        优雅Master容错处理 (再也不有2s的中止服务时间)

·        Chunk大小只有1MB (对小文件很友好)

·        Master能够存储更多的Metadata(当Chunk从64MB变为1MB后,Metadata会扩大64倍,可是Google也解决了)

Colossus能够自动分区Metadata。使用Reed-Solomon算法来复制,能够将原先的3份减少到1.5份,提升写的性能,下降延迟。客户端来复制数据。具体细节笔者也猜不出。

 

与BigTable, Megastore对比

Spanner主要致力于跨数据中心的数据复制上,同时也能提供数据库功能。在Google相似的系统有BigTable和Megastore。和这二者相比,Spanner又有什么优点呢。

BigTable在Google获得了普遍的使用,可是他不能提供较为复杂的Schema,还有在跨数据中心环境下的强一致性。 Megastore有类RDBMS的数据模型,同时也支持同步复制,可是他的吞吐量太差,不能适应应用要求。Spanner再也不是相似BigTable的 版本化 key-value存储,而是一个“临时多版本”的数据库。何为“临时多版本”,数据是存储在一个版本化的关系表里面,存储的时间数据会根据其提交的时间 打上时间戳,应用能够访问到较老的版本,另外老的版本也会被垃圾回收掉。

Google官方认为 Spanner是下一代BigTable,也是Megastore的继任者。

 

Google Spanner设计

功能

从高层看Spanner是经过Paxos状态机将分区好的数据分布在全球的。数据复制全球化的,用户能够指定数据复制的份数和存 储的地点。Spanner能够在集群或者数据发生变化的时候将数据迁移到合适的地点,作负载均衡。用户能够指定将数据分布在多个数据中心,不过更多的数据 中心将形成更多的延迟。用户须要在可靠性和延迟之间作权衡,通常来讲复制1,2个数据中心足以保证可靠性。

做为一个全球化分布式系统,Spanner提供一些有趣的特性。

·        应用能够细粒度的指定数据分布的位置。精确的指定数据离用户有多远,能够有效的控制读延迟(读延迟取决于最近 的拷贝)。指定数据拷贝之间有多远,能够控制写的延迟(写延迟取决于最远的拷贝)。还要数据的复制份数,能够控制数据的可靠性和读性能。(多写几份,能够 抵御更大的事故)

·        Spanner还有两个通常分布式数据库不具有的特性:读写的外部一致性,基于时间戳的全局的读一致。这两个特性可让Spanner支持一致的备份,一致的MapReduce,还有原子的Schema修改。

这写特性都得益有Spanner有一个全球时间同步机制,能够在数据提交的时候给出一个时间戳。由于时间是系列化的,因此才有外部一致性。这个很容易理解,若是有两个提交,一个在T1,一个在T2。那有更晚的时间戳那个提交是正确的。

这个全球时间同步机制是用一个具备GPS和原子钟的TrueTime API提供了。这个TrueTime API可以将不一样数据中心的时间误差缩短在10ms内。这个API能够提供一个精确的时间,同时给出偏差范围。Google已经有了一个TrueTime API的实现。笔者以为这个TrueTimeAPI 很是有意义,若是能单独开源这部分的话,不少数据库如MongoDB均可以从中受益。

体系结构

Spanner因为是全球化的,因此有两个其余分布式数据库没有的概念。

·        Universe。一个Spanner部署实例称之为一个Universe。目前全世界有3个。一个开发,一个测试,一个线上。由于一个Universe就能覆盖全球,不须要多个。

·        Zones. 每一个Zone至关于一个数据中心,一个Zone内部物理上必须在一块儿。而一个数据中心可能有多个Zone。能够在运行时添加移除Zone。一个Zone能够理解为一个BigTable部署实例。

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

 

 

 

如图所示。一个Spanner有上面一些组件。实际的组件确定不止这些,好比TrueTime API Server。若是仅仅知道这些知识,来构建Spanner是远远不够的。但Google都略去了。那笔者就简要介绍一下。

·        Universemaster: 监控这个universe里zone级别的状态信息

·        Placement driver:提供跨区数据迁移时管理功能

·        Zonemaster:至关于BigTable的Master。管理Spanserver上的数据。

·        Location proxy:存储数据的Location信息。客户端要先访问他才知道数据在那个Spanserver上。

·        Spanserver:至关于BigTable的ThunkServer。用于存储数据。

能够看出来这里每一个组件都颇有料,可是Google的论文里只具体介绍了Spanserver的设计,笔者也只能介绍到这里。下面详细阐述Spanserver的设计。

 

Spanserver

本章详细介绍Spanserver的设计实现。Spanserver的设计和BigTable很是的类似。参照下图

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

从下往上看。每一个数据中心会运行一套Colossus (GFS II) 。每一个机器有100-1000个tablet。Tablet概念上将至关于数据库一张表里的一些行,物理上是数据文件。打个比方,一张1000行的表,有 10个tablet,第1-100行是一个tablet,第101-200是一个tablet。但和BigTable不一样的是BigTable里面的 tablet存储的是Key-Value都是string,Spanner存储的Key多了一个时间戳:

(Key: string, timestamp: int64) ->string。

所以spanner天生就支持多版本,tablet在文件系统中是一个B-tree-like的文件和一个write-ahead日志。

每一个Tablet上会有一个Paxos状态机。Paxos是一个分布式一致性协议。Table的元数据和log都存储在上面。 Paxos会选出一个replica作leader,这个leader的寿命默认是10s,10s后重选。Leader就至关于复制数据的master, 其余replica的数据都是从他那里复制的。读请求能够走任意的replica,可是写请求只有去leader。这些replica统称为一个 paxos group。

每一个leader replica的spanserver上会实现一个lock table还管理并发。Lock table记录了两阶段提交须要的锁信息。可是不管是在Spanner仍是在BigTable上,但遇到冲突的时候长时间事务会将性能不好。因此有一些操 做,如事务读能够走lock table,其余的操做能够绕开lock table。

每一个leader replica的spanserver上还有一个transaction manager。若是事务在一个paxos group里面,能够绕过transaction manager。可是一旦事务跨多个paxos group,就须要transaction manager来协调。其中一个Transactionmanager被选为leader,其余的是slave听他指挥。这样能够保证事务。

 

Directories and Placement

之因此Spanner比BigTable有更强的扩展性,在于Spanner还有一层抽象的概念directory, directory是一些key-value的集合,一个directory里面的key有同样的前缀。更稳当的叫法是bucketing。 Directory是应用控制数据位置的最小单元,能够经过谨慎的选择Key的前缀来控制。据此笔者能够猜出,在设计初期,Spanner是做为F1的存 储系统而设立,甚至还设计有相似directory的层次结构,这样的层次有不少好处,可是实现太复杂被摒弃了。

Directory做为数据放置的最小单元,能够在paxos group里面移来移去。Spanner移动一个directory通常出于以下几个缘由:

·        一个paxos group的负载太大,须要切分

·        将数据移动到access更近的地方

·        将常常同时访问的directory放到一个paxos group里面

Directory能够在不影响client的前提下,在后台移动。移动一个50MB的directory大概须要的几秒钟。

那么directory和tablet又是什么关系呢。能够理解为Directory是一个抽象的概念,管理数据的单元;而 tablet是物理的东西,数据文件。因为一个Paxos group可能会有多个directory,因此spanner的tablet实现和BigTable的tablet实现有些不一样。BigTable的 tablet是单个顺序文件。Google有个项目,名为Level DB,是BigTable的底层,能够看到其实现细节。而Spanner的tablet能够理解是一些基于行的分区的容器。这样就能够将一些常常同时访问 的directory放在一个tablet里面,而不用太在乎顺序关系。

在paxos group之间移动directory是后台任务。这个操做还被用来移动replicas。移动操做设计的时候不是事务的,由于这样会形成大量的读写 block。操做的时候是先将实际数据移动到指定位置,而后再用一个原子的操做更新元数据,完成整个移动过程。

Directory仍是记录地理位置的最小单元。数据的地理位置是由应用决定的,配置的时候须要指定复制数目和类型,还有地理的 位置。好比(上海,复制2份;南京复制1分) 。这样应用就能够根据用户指定终端用户实际状况决定的数据存储位置。好比中国队的数据在亚洲有3份拷贝, 日本队的数据全球都有拷贝。

前面对directory仍是被简化过的,还有不少没法详述。

 

数据模型

Spanner的数据模型来自于Google内部的实践。在设计之初,Spanner就决心有如下的特性:

·        支持相似关系数据库的schema

·        Query语句

·        支持广义上的事务

为什么会这样决定呢?在Google内部还有一个Megastore,尽管要忍受性能不够的折磨,可是在Google有300多个 应用在用它,由于Megastore支持一个相似关系数据库的schema,并且支持同步复制 (BigTable只支持最终一致的复制) 。使用Megastore的应用有大名鼎鼎的Gmail, Picasa, Calendar, Android Market和AppEngine。 而必须对Query语句的支持,来自于广受欢迎的Dremel,笔者不久前写了篇文章来介绍他。 最后对事务的支持是比不可少了,BigTable在Google内部被抱怨的最多的就是其只能支持行事务,再大粒度的事务就无能为力了。Spanner的 开发者认为,过分使用事务形成的性能降低的恶果,应该由应用的开发者承担。应用开发者在使用事务的时候,必须考虑到性能问题。而数据库必须提供事务机制, 而不是由于性能问题,就干脆不提供事务支持。

数据模型是创建在directory和key-value模型的抽象之上的。一个应用能够在一个universe中创建一个或多 个database,在每一个database中创建任意的table。Table看起来就像关系型数据库的表。有行,有列,还有版本。Query语句看起 来是多了一些扩展的SQL语句。

Spanner的数据模型也不是纯正的关系模型,每一行都必须有一列或多列组件。看起来仍是Key-value。主键组成Key,其余的列是Value。但这样的设计对应用也是颇有裨益的,应用能够经过主键来定位到某一行。

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的背景,设计和并发控制。但愿不久的未来,会有开源产品出现。