标签:
云计算数据库杂谈 |
独家供稿:移动Labs
Google Spanner简介
Spanner
Spanner是个可扩展,多版本,全球分布式还支持同步复制的数据库。他是Google的第一个可以全球扩展并且支持外部一致的事务。Spanner能 做到这些,离不开一个用GPS和原子钟实现的时间API。这个API能将数据中心之间的时间同步精确到10ms以内。因此有几个给力的功能:无锁读事务, 原子schema修改,读历史数据无block。
EMC中国研究院实时紧盯业界动态,Google最近发布的一篇论文《Spanner: Google’s Globally-Distributed
Database》,
下文主要是Spanner的背景,设计和并发控制。
Spanner背景
要搞清楚Spanner原理,先得了解Spanner在Google的定位。
从上图可以看到。Spanner位于F1和GFS之间,承上启下。所以先提一提F1和GFS。
F1
和众多互联网公司一样,在早期Google大量使用了Mysql。Mysql是单机的,可以用Master-Slave来容错,分区来扩展。但是需 要大量的手工运维工作,有很多的限制。因此Google开发了一个可容错可扩展的RDBMS——F1。和一般的分布式数据库不同,F1对应RDMS应有的 功能,毫不妥协。起初F1是基于Mysql的,不过会逐渐迁移到Spanner。
F1有如下特点:
·
· 可以同时提供强一致性和弱一致。
· 可扩展
· 支持SQL
· 事务提交延迟50-100ms,读延迟5-10ms,高吞吐
众所周知Google BigTable是重要的NoSql产品,提供很好的扩展性,开源世界有HBase与之对应。为什么Google还需要F1,而不是都使用
Colossus(GFS II)
Colossus也是一个不得不提起的技术。他是第二代GFS,对应开源世界的新HDFS。GFS是著名的分布式文件系统。
Colossus是第二代GFS。Colossus是Google重要的基础设施,因为他可以满足主流应用对FS的要求。Colossus的重要改进有:
· 优雅Master容错处理
·
·
Colossus可以自动分区Metadata。使用Reed-Solomon算法来复制,可以将原先的3份减小到1.5份,提高写的性能,降低延迟。客户端来复制数据。具体细节笔者也猜不出。
与BigTable,
Spanner主要致力于跨数据中心的数据复制上,同时也能提供数据库功能。在Google类似的系统有BigTable和Megastore。和这两者相比,Spanner又有什么优势呢。
BigTable在Google得到了广泛的使用,但是他不能提供较为复杂的Schema,还有在跨数据中心环境下的强一致性。Megastore
Google官方认为
Google Spanner设计
功能
从高层看Spanner是通过Paxos状态机将分区好的数据分布在全球的。数据复制全球化的,用户可以指定数据复制的份数和存储的地点。
作为一个全球化分布式系统,Spanner提供一些有趣的特性。
· 应用可以细粒度的指定数据分布的位置。精确的指定数据离用户有多远,可以有效的控制读延迟(读延迟取决于最近的拷贝)。指定数据拷贝之间有多远,可以控制 写的延迟(写延迟取决于最远的拷贝)。还要数据的复制份数,可以控制数据的可靠性和读性能。(多写几份,可以抵御更大的事故)
·
这写特性都得益有Spanner有一个全球时间同步机制,可以在数据提交的时候给出一个时间戳。因为时间是系列化的,所以才有外部一致性。这个很容易理解,如果有两个提交,一个在T1,一个在T2。那有更晚的时间戳那个提交是正确的。
这个全球时间同步机制是用一个具有GPS和原子钟的TrueTime API提供了。这个TrueTime API能够将不同数据中心的时间偏差缩短在10ms内。这个API可以提供一个精确的时间,同时给出误差范围。Google已经有了一个TrueTime API的实现。笔者觉得这个TrueTimeAPI
体系结构
Spanner由于是全球化的,所以有两个其他分布式数据库没有的概念。
·
·
如图所示。一个Spanner有上面一些组件。实际的组件肯定不止这些,比如TrueTime API Server。如果仅仅知道这些知识,来构建Spanner是远远不够的。但Google都略去了。那笔者就简要介绍一下。
·
·
·
·
·
可以看出来这里每个组件都很有料,但是Google的论文里只具体介绍了Spanserver的设计,笔者也只能介绍到这里。下面详细阐述Spanserver的设计。
Spanserver
本章详细介绍Spanserver的设计实现。Spanserver的设计和BigTable非常的相似。参照下图
(Key: string, timestamp: int64) ->string。
因此spanner天生就支持多版本,tablet在文件系统中是一个B-tree-like的文件和一个write-ahead日志。
每个Tablet上会有一个Paxos状态机。Paxos是一个分布式一致性协议。Table的元数据和log都存储在上面。Paxos会选出一个
每个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作为数据放置的最小单元,可以在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的
在paxos group之间移动directory是后台任务。这个操作还被用来移动replicas。移动操作设计的时候不是事务的,因为这样会造成大量的读写
Directory还是记录地理位置的最小单元。数据的地理位置是由应用决定的,配置的时候需要指定复制数目和类型,还有地理的位置。比如(上海, 复制2份;南京复制1分)
前面对directory还是被简化过的,还有很多无法详述。
数据模型
Spanner的数据模型来自于Google内部的实践。在设计之初,Spanner就决心有以下的特性:
· 支持类似关系数据库的schema
·
· 支持广义上的事务
为何会这样决定呢?在Google内部还有一个Megastore,尽管要忍受性能不够的折磨,但是在Google有300多个应用在用它,因为
数据模型是建立在directory和key-value模型的抽象之上的。一个应用可以在一个universe中建立一个或多个
Spanner的数据模型也不是纯正的关系模型,每一行都必须有一列或多列组件。看起来还是Key-value。主键组成Key,其他的列是Value。但这样的设计对应用也是很有裨益的,应用可以通过主键来定位到某一行。
TrueTime
那这个TrueTime API实现靠的是GFS和原子钟。之所以要用两种技术来处理,是因为导致这两个技术的失败的原因是不同的。GPS会有一个天线,电波干扰会导致其失灵。原子钟很稳定。当GPS失灵的时候,原子钟仍然能保证在相当长的时间内,不会出现偏差。
实际部署的时候。每个数据中心需要部署一些Master机器,其他机器上需要有一个slave进程来从Master同步。有的Master用
每个Slave后台进程会每个30秒从若干个Master更新自己的时钟。为了降低误差,使用Marzullo算法。每个slave还会计算出自己的误差。这里的误差包括的通信的延迟,机器的负载。如果不能访问Master,误差就会越走越大,知道重新可以访问。
Google Spanner并发控制
Spanner使用TrueTime来控制并发,实现外部一致性。支持以下几种事务。
· 读写事务
· 只读事务
· 快照读,客户端提供时间戳
· 快照读,客户端提供时间范围
例如一个读写事务发生在时间t,那么在全世界任何一个地方,指定t快照读都可以读到写入的值。
时间戳的设计大大提高了只读事务的性能。事务开始的时候,要声明这个事务里没有写操作,只读事务可不是一个简单的没有写操作的读写事务。它会用一个 系统时间戳去读,所以对于同时的其他的写操作是没有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将提交时间戳发送给客户端还有其他的
只读事务
对于只读事务,Spanner首先要指定一个读事务时间戳。还需要了解在这个读操作中,需要访问的所有的读的Key。Spanner可以自动确定Key的范围。
如果Key的范围在一个Paxos group内。客户端可以发起一个只读请求给group leader。leader选一个时间戳,这个时间戳要比上一个事务的结束时间要大。然后读取相应的数据。这个事务可以满足外部一致性,读出的结果是最后 一次写的结果,并且不会有不一致的数据。
如果Key的范围在多个Paxos group内,就相对复杂一些。其中一个比较复杂的例子是,可以遍历所有的group leaders,寻找最近的事务发生的时间,并读取。客户端只要时间戳在TT.now().latest之后就可以满足要求了。
最后的话
本文介绍了GoogleSpanner的背景,设计和并发控制。希望不久的将来,会有开源产品出现。
原文出处:全球级的分布式数据库 Google Spanner原理
本博文发表在移动Labs的文链是:http://labs.chinamobile.com/mblog/393521/184280
【相关博文】
MySQL源代码阅读(1)
云计算存储一生的短彩信,私藏人生的回忆
【精彩推荐】
中国移动研究院2013校园招聘之宣讲行程更新、简历接收截止时间公布!
iPhone 5体现库克风格 苹果战略改变?
中国通信业10年纪事:最善变的市场
微软、诺基亚和HTC的三角恋
怎样找到适合自己的减压方法?
想了解一下国内平板电脑wifi版和移动网络版各自的占比情况,有人有相关的数据吗?
套餐(如全球通套餐、短信套餐、流量套餐)的理论基础是什么?
“用户体验”和“用户界面”的差异是什么?
云计算将对软件外包行业产生怎样的影响?