|
7.3 Google Spanner
: t9 E/ D! z* w. ~& IGoogle Spanner是Google的全球级分布式数据库(Globally-Distributed7 y* j& H) } f& q
Database)。Spanner的扩展性达到了全球级,可以扩展到数百个数据中心,数百万6 r8 k0 S' c4 g9 @
台机器,上万亿行记录。更为重要的是,除了夸张的可扩展性之外,它还能通过同
. r$ J3 L+ E. `6 l( c" T: ~步复制和多版本控制来满足外部一致性,支持跨数据中心事务。
. j8 K' p/ ~ o6 Z) ]' l无论从学术研究还是工程实践的角度看,Spanner都是一个划时代的分布式存储' n% Z6 h/ @2 j" l
系统。Spanner的成功说明了一点,分布式技术能够和数据库技术有机地结合起来,% ?3 X! I" x; Y
通过分布式技术实现高可扩展性,并呈现给使用者类似关系数据库的数据模型。2 j8 i/ ^7 x/ Y1 t' Z' n; L
7.3.1 数据模型, f, Q4 W0 O# I8 J1 M
Spanner的数据模型与6.2节中介绍的Megastore系统比较类似。5 \- P- h+ G* s: x: d! X( m; m
如图7-6所示,对于一个典型的相册应用,需要存储其用户和相册,可以用上面
- N7 Z% A+ n: L的两个SQL语句来创建表。Spanner的表是层次化的,最底层的表是目录表
5 \2 i, ~4 g' Z# B0 } n(Directorytable),其他表创建时,可以用INTERLEAVE IN PARENT来表示层次关
H- p4 M4 a+ I1 `$ s3 i$ R系。Spanner中的目录相当于Megastore中的实体组,一个用户的信息(user_id,email)8 ?4 F1 u* ~4 [# P( o0 W1 m
以及这个用户下的所有相片信息构成一个目录。实际存储时,Spanner会将同一个目0 `+ s* J- J/ M; X C- N# I9 J+ E" o
录的数据存放到一起,只要目录不太大,同一个目录的每个副本都会分配到同一台/ }: ]" O* {1 Q w! n5 p h5 d/ e
机器。因此,针对同一个目录的读写事务大部分情况下都不会涉及跨机操作。& v% O. s! L6 I! @) x; g* b( N+ w9 F4 {
图 7-6 Spanner数据模型
, l( L$ _6 I" D: r6 i/ K7.3.2 架构% W, b. W: ?2 u* e6 [
Spanner构建在Google下一代分布式文件系统Colossus之上。Colossus是GFS的延
7 @: s4 l! N3 r+ z k, {, I2 n续,相比GFS,Colossus的主要改进点在于实时性,并且支持海量小文件。+ F3 \5 K# D( V8 H) U6 T
由于Spanner是全球性的,因此它有两个其他分布式存储系统没有的概念:3 ]6 a; Y% w) k7 {0 c( N7 y! ]& e
●Universe。一个Spanner部署实例称为一个Universe。目前全世界有3个,一个开& g4 e, y) o3 I: W) a
发、一个测试、一个线上。Universe支持多数据中心部署,且多个业务可以共享同一
p+ F1 R; E ^. Y+ B个Universe。
; Q# n* P: S# I3 r$ i8 k6 x* X# ^●Zones。每个Zone属于一个数据中心,而一个数据中心可能有多个Zone。一般
% J; J7 @: Z$ S$ s9 W来说,Zone内部的网络通信代价较低,而Zone与Zone之间通信代价很高。
0 ^; o, `4 Y. _! H8 w如图7-7所示,Spanner系统包含如下组件:5 k6 L5 p. Y; X1 n! i5 r
图 7-7 Spanner整体架构' G1 K) S7 X0 g5 g3 O6 }: S& m
●Universe Master:监控这个Universe里Zone级别的状态信息。/ W5 S7 }6 g ?
●Placement Driver:提供跨Zone数据迁移功能。1 ~! L O2 p( z, K
●Location Proxy:提供获取数据的位置信息服务。客户端需要通过它才能够知道
/ T& Y5 ^8 Y# }2 R1 n! k, @数据由哪台Spanserver服务。
1 o5 C/ h& S8 Y' q- z●Spanserver:提供存储服务,功能上相当于Bigtable系统中的Tablet Server。
+ b8 ]/ R) k; Z) A每个Spanserver会服务多个子表,而每个子表又包含多个目录。客户端往Spanner
' k2 T" L; G# X& T ~发送读写请求时,首先查找目录所在的Spanserver,接着从Spanserver读写数据。
- S9 O) t x, m! e0 p: c( I这里面有一个问题:如何存储目录与Spanserver之间的映射关系?假设每个用户* A _7 l: M- m& R; G" r
对应一个目录,全球总共有50亿用户,那么,映射关系的数据规模为几十亿到几百' V* K2 M3 {/ y" M$ `& i1 ?
亿,单台服务器无法存放。Spanner论文中没有明确说明,笔者猜测这里的做法和$ L; m: H: I/ d5 H, E
Bigtable类似,即将映射关系这样的元数据信息当成元数据表格,和普通用户表格采
. d# F( Y+ G7 Z6 h( j, g取相同的存储方式。
8 _' U# l, E/ \5 a7.3.3 复制与一致性
7 u( o: a+ \6 ?" y如图7-8所示,每个数据中心运行着一套Colossus,每个机器有100~1000个子, p9 ]* C6 ~7 q3 B
表,每个子表会在多个数据中心部署多个副本。为了同步系统中的操作日志,每个' ?1 v9 @; f) d) d( R2 r% K
子表上会运行一个Paxos状态机。Paxos协议会选出一个副本作为主副本,这个主副本
0 v* X8 h3 ?: p* g* ^& a1 U的寿命默认是10秒。正常情况下,这个主副本会在快要到期的时候将自己再次选为7 `# _& D: v1 L' t$ }
主副本;如果出现异常,例如主副本所在的spanserver宕机,其他副本会在10秒后通, I5 }' P: ]; N4 Y+ X% q1 v; X0 \
过Paxos协议选举为新的主副本。
$ |( t& [) s9 X$ [图 7-8 Spanner多集群复制# q. c$ b/ P- Q1 _
通过Paxos协议,实现了跨数据中心的多个副本之间的一致性。另外,每个主副9 q" p6 |& a9 @5 l, j" @- c
本所在的Spanserver还会实现一个锁表用于并发控制,读写事务操作某个子表上的目+ {4 q# \( }: E: H) G
录时需要通过锁表避免多个事务之间互相干扰。
$ i3 N- e6 U% z) I$ e8 K# O除了锁表,每个主副本上还有一个事务管理器。如果事务在一个Paxos组里面,
9 V! O. d1 P+ Y, T) R* ^# o! B可以绕过事务管理器。但是一旦事务跨多个Paxos组,就需要事务管理器来协调。
; Y }! E2 D( [0 m) u锁表实现单个Paxos组内的单机事务,事务管理器实现跨多个Paxos组的分布式事
) t2 w: \6 p: e务。为了实现分布式事务,需要实现3.7.1节中提到的两阶段提交协议。有一个Paxos
) `7 m3 X5 E- {, b; d1 K; t组的主副本会成为两阶段提交协议中的协调者,其他Paxos组的主副本为参与者。
4 W; X; `4 D/ t/ K& s$ K, a1 V! y; J( C7.3.4 TrueTime
5 U* q7 D+ u- _- T0 k为了实现并发控制,数据库需要给每个事务分配全局唯一的事务id。然而,在分
, r) d! Q2 j% d布式系统中,很难生成全局唯一id。一种方式是采用Google Percolator(Google* i- x9 h& K# E
Caffeine的底层存储系统)中的做法,即专门部署一套Oracle数据库用于生成全局唯/ {4 j1 G( ?: L% x8 o
一id。虽然Oracle逻辑上是一个单点,但是实现的功能单一,因而能够做得很高效。! E8 w- t# T7 A( h* E
Spanner选择了另外一种做法,即全球时钟同步机制TrueTime。' z7 ]2 V }% | `% f/ `
TrueTime是一个提供本地时间的接口,但与Linux上的gettimeofday接口不一样的: b0 e+ n( i* d; v
是,它除了可以返回一个时间戳t,还会给出一个误差e。例如,返回的时间戳是20点+ p% y' C& M* t# R$ o
23分30秒100毫秒,而误差是5毫秒,那么真实的时间在20点23分30秒95毫秒到105毫
4 C& `1 B6 P6 P; B- G秒之间。真实的系统e平均下来只有4毫秒。# A e( h7 k+ T, ^4 S6 P. v
TrueTime API实现的基础是GPS和原子钟。之所以要用两种技术来处理,是因为
5 m* o5 v6 Y' I导致这两种技术失效的原因是不同的。GPS会有一个天线,电波干扰会导致其失灵。
7 G- i% w8 v6 @4 d) ]7 c* u原子钟很稳定。当GPS失灵的时候,原子钟仍然能保证在相当长的时间内,不会出现& _" k' c' S( G+ g3 _/ z3 X
偏差。
& z5 m5 @! m. l每个数据中心需要部署一些主时钟服务器(Master),其他机器上部署一个从时$ n# m3 u z" T8 a* y9 C/ b
钟进程(Slave)来从主时钟服务器同步时钟信息。有的主时钟服务器用GPS,有的* Z1 @! ?/ _. o
主时钟服务器用原子钟。每个从时钟进程每隔30秒会从若干个主时钟服务器同步时6 E; _& Q/ f3 h- j! w' |
钟信息。主时钟服务器自己还会将最新的时间信息和本地时钟比对,排除掉偏差比; G' X& Y. b0 l' h, Y& C7 i0 h- }
较大的结果。, Q+ n- O( h0 y
7.3.5 并发控制
4 {: ~+ `* t& ^4 i2 ~% _% ISpanner使用TrueTime来控制并发,实现外部一致性,支持以下几种事务:
! E& a7 e3 Y8 E+ B/ _4 H9 ^●读写事务
4 @& \3 B, {7 F1 G. j" L: w●只读事务
1 E) o5 g& G% i6 g8 k●快照读,客户端提供时间戳5 w/ _% {, l' N7 D/ A
●快照读,客户端提供时间范围2 g$ M( Q. _/ w" \
1.不考虑TrueTime( T, P& r. M" l x) r3 q' g5 s
首先,不考虑TrueTime的影响,也就是说,假设TrueTime API获得的时间是精确
$ [' Z# w6 H' V2 L$ p6 G+ l的。如果事务读写的数据只属于同一个Paxos组,那么,每个读写事务的执行步骤如
g& R3 F. t' u7 D# _下:
* f4 U4 ]# \% ]% j7 h0 h+ B1)获取系统的当前时间戳;
( A. @' f s, k4 a2)执行读写操作,并将第1步取得的时间戳作为事务的提交版本。
2 z; Z$ _* k7 T2 w0 i每个只读事务的执行步骤如下:# J7 A, T) u0 x; X* e0 m- b
1)获取系统的当前时间戳,作为读事务的版本;8 o. c' X8 T1 Y; h
2)执行读取操作,返回客户端所有提交版本小于读事务版本的事务操作结果。
% |$ P7 Z ~5 W+ }# _快照读和只读事务的区别在于:快照读将指定读事务的版本,而不是取系统的# @1 \/ H& x% Y$ B/ Z
当前时间戳。- a# J0 t/ Y. S3 p7 z% `# l
如果事务读写的数据涉及多个Paxos组,那么,对于读写事务,需要执行一次两# \5 i; J3 i- K0 i% k- g, P: Z
阶段提交协议,执行步骤如下:" q, h+ V+ m, [* k6 i/ o9 z
1)Prepare:客户端将数据发往多个Paxos组的主副本,同时,协调者主副本发* ^1 o1 J6 Q" C, C- G* m. A
起prepare协议,请求其他的参与者主副本锁住需要操作的数据。+ [2 M6 j* N$ Y( W) M; b2 ~
2)Commit:协调者主副本发起Commit协议,要求每个参与者主副本执行提交操
3 e, l' v# d8 c" m7 L. e作并解除Prepare阶段锁定的数据。协调者主副本可以将它的当前时间戳作为该事务
6 Z& k3 D: S: f# ^: R的提交版本,并发送给每个参与者主副本。
" i5 {* I2 Y+ e3 \; |$ Y只读事务读取每个Paxos组中提交版本小于读事务版本的事务操作结果。需要注! k2 ]1 m4 m; i! j. n) O% C! v
意的是,只读事务需要保证不会读到不完整的事务。假设有一个读写事务修改了两& O* a1 K1 h) v2 m* G
个Paxos组:Paxos组A和Paxos组B,Paxos组A上的修改已提交,Paxos组B上的修改还未
2 O6 Z0 j$ x4 Z7 \9 _- `$ p9 j提交。那么,只读事务会发现Paxos组B处于两阶段提交协议中的Prepare阶段,需要
- P, h' E4 w2 _& [9 m. r6 W等待一会,直到Paxos组B上的修改生效后才能读到正确的数据。
! c$ Y5 g/ N' A0 X4 P2.考虑TrueTime; x7 v" q! e' v7 E
如果考虑TrueTime,并发控制变得复杂。这里的核心思想在于,只要事务T1的% U a1 X7 O; b+ T
提交操作早于事务T2的开始操作,即使考虑TrueTime API的误差因素(-e到+e之间,
. S7 Q3 k7 F( }: q0 ce值平均为4ms),Spanner也能保证事务T1的提交版本小于事务T2的提交版本。
9 G' C! F" i- n5 uSpanner使用了一种称为延迟提交(Commit Wait)的手段,即如果事务T1的提交版本
+ Z) t) j$ E+ Y7 E) I为时间戳t commit ,那么,事务T1会在t commit +e之后才能提交。另外,如果事务T2开始
5 _& D& ]0 k* P4 U2 _3 k! Z+ F* D的绝对时间为t abs ,那么事务T2的提交版本至少为t abs +e。这样,就保证了事务T1和T2
. w8 y. C$ Y. r' E之间严格的顺序,当然,这也意味着每个写事务的延时至少为2e。从这一点也可以( @' v! h5 I' r h' }
看出,Spanner实现功能完备的全球数据库是付出了一定代价的,设计架构时不能盲
4 B$ s- A- ]7 S! U. I. a3 S% ?& _目崇拜。
6 O! p# h7 Y4 a) ^& L7.3.6 数据迁移
2 C5 p: B; I% V! j/ E2 d目录是Spanner中对数据分区、复制和迁移的基本单位,用户可以指定一个目录6 h1 Z0 `4 _$ W9 Y6 q
有多少个副本,分别存放在哪些机房中,例如将用户的目录存放在这个用户所在地
7 C! c) S+ u3 k( r9 ^3 D4 O0 y区附近的几个机房中。# v; G8 p9 [. o' {' d
一个Paxos组包含多个目录,目录可以在Paxos组之间移动。Spanner移动一个目
, u0 f$ y9 r5 ?2 N4 u& b! x# D; M录一般出于以下几种考虑:
; E0 E1 g4 O4 Q9 n: |●某个Paxos组的负载太大,需要切分;
; m; X3 g: Z; ~! B●将数据移动到离用户更近的地方,减少访问延时;( }4 F& ^+ j, O2 l& ?
●把经常一起访问的目录放进同一个Paxos组。; n. [! \, q, E9 i, n2 }1 U
移动目录的操作在后台进行,不影响前台的客户端读写操作。一般来说,移动
6 s8 v1 r( I0 c, ~- [. Y5 f+ D% U0 S一个50MB的目录大约只需要几秒钟时间。实现时,首先将目录的实际数据移动到指
& j* a" x4 F& h8 [定位置,然后再用一个原子操作更新元数据,从而完成整个移动过程。; @3 K$ I7 b3 U
7.3.7 讨论
7 q* Q8 C9 t. M1 _$ c7 W1 N, HGoogle的分布式存储系统一步步地从Bigtable到Megastore,再到Spanner,这也印
7 T8 k- V) j5 X# g' b7 B证了分布式技术和传统关系数据库技术融合的必然性,即底层通过分布式技术实现
9 ~& @3 T, x8 `4 {- ^可扩展性,上层通过关系数据库的模型和接口将系统的功能暴露给用户。
& C% [( r# R* l) ^2 L% f阿里巴巴的OceanBase系统在设计之初就考虑到这两种技术融合的必然性,因
! P) d A( p2 X/ Y5 P此,一开始就将系统的最终目标定为:可扩展的关系数据库。目前,OceanBase已经9 _: S2 s3 m) ~
开发完成了部分功能并在阿里巴巴各个子公司获得广泛的应用。本书第三篇将详细
6 V1 @* U$ t8 f7 [介绍OceanBase的技术细节。7 N7 n" j- i1 s
* R2 C1 `8 w0 m9 X" p7 S% z4 S& W+ E7 } {/ h2 X
|
|