|
7.3 Google Spanner8 g5 F8 D: r; l' w
Google Spanner是Google的全球级分布式数据库(Globally-Distributed2 Y. W" K" L- ^7 a# S* P
Database)。Spanner的扩展性达到了全球级,可以扩展到数百个数据中心,数百万+ j. R, V' t' I) Z0 t# j. z8 Q/ u
台机器,上万亿行记录。更为重要的是,除了夸张的可扩展性之外,它还能通过同! k1 U. L+ _5 J: M7 F5 H; m1 |
步复制和多版本控制来满足外部一致性,支持跨数据中心事务。
" w' U- `6 Q, U& n无论从学术研究还是工程实践的角度看,Spanner都是一个划时代的分布式存储+ S' h' ?7 q& m3 \0 A7 G* e/ x5 v
系统。Spanner的成功说明了一点,分布式技术能够和数据库技术有机地结合起来,# K1 ?+ ?* I y9 j8 H _+ C1 z! E: R+ o
通过分布式技术实现高可扩展性,并呈现给使用者类似关系数据库的数据模型。( G5 Y* x6 D1 |: P4 t6 U
7.3.1 数据模型; y$ r* M/ }2 |* y m
Spanner的数据模型与6.2节中介绍的Megastore系统比较类似。
! W" A$ P6 _; O' F$ u. L7 J0 p如图7-6所示,对于一个典型的相册应用,需要存储其用户和相册,可以用上面* {1 m: Z& j$ I3 k5 p5 K
的两个SQL语句来创建表。Spanner的表是层次化的,最底层的表是目录表
1 ~+ Y5 s# j% G, `0 |(Directorytable),其他表创建时,可以用INTERLEAVE IN PARENT来表示层次关3 K+ t l7 Q8 t
系。Spanner中的目录相当于Megastore中的实体组,一个用户的信息(user_id,email)
! o8 c$ A: X! S) A# w( I7 N以及这个用户下的所有相片信息构成一个目录。实际存储时,Spanner会将同一个目
3 v6 V8 \3 B+ _录的数据存放到一起,只要目录不太大,同一个目录的每个副本都会分配到同一台
: P5 K$ u8 a. k, C5 C' v0 f0 D机器。因此,针对同一个目录的读写事务大部分情况下都不会涉及跨机操作。' s1 |- u1 {+ @: D
图 7-6 Spanner数据模型1 ?( h( @& R2 @2 u& ^8 x# o6 Y/ e
7.3.2 架构- Q+ ~% v W2 E* ^
Spanner构建在Google下一代分布式文件系统Colossus之上。Colossus是GFS的延
; ]. \- }4 E$ I7 B" y, m: [8 I续,相比GFS,Colossus的主要改进点在于实时性,并且支持海量小文件。
# f4 w" G. Z3 h; ~/ p( i& H" j由于Spanner是全球性的,因此它有两个其他分布式存储系统没有的概念:+ h) E8 p9 p; T1 K
●Universe。一个Spanner部署实例称为一个Universe。目前全世界有3个,一个开
: Q M1 V$ S2 e! ~发、一个测试、一个线上。Universe支持多数据中心部署,且多个业务可以共享同一
; K- L/ \. M* |$ |8 I: B# u个Universe。
8 U5 h* ^- T' R' b% ?8 B# i●Zones。每个Zone属于一个数据中心,而一个数据中心可能有多个Zone。一般4 `0 V* g2 F- |3 H$ z
来说,Zone内部的网络通信代价较低,而Zone与Zone之间通信代价很高。, i9 v# f; L& j7 W# [5 H. a
如图7-7所示,Spanner系统包含如下组件:
; \; h( ~2 _. c图 7-7 Spanner整体架构
0 X! N1 S! L( K; {●Universe Master:监控这个Universe里Zone级别的状态信息。& Y, T* [7 U5 u3 v
●Placement Driver:提供跨Zone数据迁移功能。 X. |8 O+ t/ [ x
●Location Proxy:提供获取数据的位置信息服务。客户端需要通过它才能够知道
; s" A( X' X4 d数据由哪台Spanserver服务。
+ h, g1 o8 m+ _●Spanserver:提供存储服务,功能上相当于Bigtable系统中的Tablet Server。9 j) L' ], D/ Y
每个Spanserver会服务多个子表,而每个子表又包含多个目录。客户端往Spanner
% B* n& M6 I; L( |" h% a% z# \发送读写请求时,首先查找目录所在的Spanserver,接着从Spanserver读写数据。8 ? k8 \& y4 e: c
这里面有一个问题:如何存储目录与Spanserver之间的映射关系?假设每个用户) v) u+ |3 B; `6 @& _: \* G
对应一个目录,全球总共有50亿用户,那么,映射关系的数据规模为几十亿到几百) E. S. q4 O9 B2 x& D% P' [8 v
亿,单台服务器无法存放。Spanner论文中没有明确说明,笔者猜测这里的做法和+ F# C @" C4 R" @- X9 a: J5 h& L
Bigtable类似,即将映射关系这样的元数据信息当成元数据表格,和普通用户表格采! l) U+ v+ G, Y" j
取相同的存储方式。
" ]: E0 m" J v3 v9 b7.3.3 复制与一致性
; k+ c0 g# G" b$ d# g如图7-8所示,每个数据中心运行着一套Colossus,每个机器有100~1000个子
9 K6 v4 q- |' e; D$ U7 Q/ R& p. }4 {9 n表,每个子表会在多个数据中心部署多个副本。为了同步系统中的操作日志,每个$ g# f A) Q$ Z$ R7 l
子表上会运行一个Paxos状态机。Paxos协议会选出一个副本作为主副本,这个主副本
& O4 V1 _9 c. g* ?5 U的寿命默认是10秒。正常情况下,这个主副本会在快要到期的时候将自己再次选为
; I4 T& E: f0 V) ^, W主副本;如果出现异常,例如主副本所在的spanserver宕机,其他副本会在10秒后通. x& P+ G \, U) B
过Paxos协议选举为新的主副本。# ?8 ]$ Q& }, z' Q2 [. w4 v g
图 7-8 Spanner多集群复制
; L! C& K* a8 @5 x通过Paxos协议,实现了跨数据中心的多个副本之间的一致性。另外,每个主副
1 B0 V, `3 y8 t# e7 L. H# c本所在的Spanserver还会实现一个锁表用于并发控制,读写事务操作某个子表上的目& b7 B$ H. y% x; t
录时需要通过锁表避免多个事务之间互相干扰。
. _2 u/ [, ^- d- h8 Z0 A除了锁表,每个主副本上还有一个事务管理器。如果事务在一个Paxos组里面,1 G4 F0 W% V2 ?" G! `" A- @# s, B0 W
可以绕过事务管理器。但是一旦事务跨多个Paxos组,就需要事务管理器来协调。
+ D Y* r, u& x8 [) W5 x锁表实现单个Paxos组内的单机事务,事务管理器实现跨多个Paxos组的分布式事
4 S" H- k: H4 H9 K( ?0 P) R4 s+ i7 j务。为了实现分布式事务,需要实现3.7.1节中提到的两阶段提交协议。有一个Paxos( O. p( q3 \5 d4 P. r. ?: _/ ]
组的主副本会成为两阶段提交协议中的协调者,其他Paxos组的主副本为参与者。
/ M- h5 l, `( u m0 Q7.3.4 TrueTime* A; N% q( ]) x2 I( \6 i# _
为了实现并发控制,数据库需要给每个事务分配全局唯一的事务id。然而,在分
8 G N4 y& Z' Z6 M3 G2 [9 [布式系统中,很难生成全局唯一id。一种方式是采用Google Percolator(Google
% I9 y- k! [ d7 ~" {4 iCaffeine的底层存储系统)中的做法,即专门部署一套Oracle数据库用于生成全局唯
$ r7 _2 X5 a/ i v一id。虽然Oracle逻辑上是一个单点,但是实现的功能单一,因而能够做得很高效。
?" s. P# k vSpanner选择了另外一种做法,即全球时钟同步机制TrueTime。
/ e K7 `6 v) R) k* H1 W7 MTrueTime是一个提供本地时间的接口,但与Linux上的gettimeofday接口不一样的, ]* _5 U( x8 X* S: J
是,它除了可以返回一个时间戳t,还会给出一个误差e。例如,返回的时间戳是20点, l7 _6 g1 s, p7 ]2 v% E# E6 C1 a
23分30秒100毫秒,而误差是5毫秒,那么真实的时间在20点23分30秒95毫秒到105毫0 [, H0 q+ H' r- U" g
秒之间。真实的系统e平均下来只有4毫秒。
/ b* \( z, ]/ R4 R8 HTrueTime API实现的基础是GPS和原子钟。之所以要用两种技术来处理,是因为
- v% l- o* P; i3 @% ?$ P+ o导致这两种技术失效的原因是不同的。GPS会有一个天线,电波干扰会导致其失灵。
3 h" E+ |4 E: ?原子钟很稳定。当GPS失灵的时候,原子钟仍然能保证在相当长的时间内,不会出现2 F4 c' n% V4 ] a9 O
偏差。
+ W, H) x6 F3 R- \$ A每个数据中心需要部署一些主时钟服务器(Master),其他机器上部署一个从时
: N2 B& m, H0 C' C7 W3 s8 c; F钟进程(Slave)来从主时钟服务器同步时钟信息。有的主时钟服务器用GPS,有的; f7 a! O# E# I6 v! X$ ~( w2 j1 c
主时钟服务器用原子钟。每个从时钟进程每隔30秒会从若干个主时钟服务器同步时& e' s- `1 T l& X& y9 ^* v- S
钟信息。主时钟服务器自己还会将最新的时间信息和本地时钟比对,排除掉偏差比
9 ?2 Q: _! ]- {! Z0 R; {* C/ E$ Y较大的结果。
& T' X2 k8 X6 T+ ~: L) F7.3.5 并发控制; }+ I( ^6 ^3 j4 n
Spanner使用TrueTime来控制并发,实现外部一致性,支持以下几种事务:8 b* H3 x9 Z2 |! V
●读写事务
8 \& I4 H& u" ?" F●只读事务
1 o" M* I% R$ l3 w. F- V7 `●快照读,客户端提供时间戳9 K! k) y6 t! j
●快照读,客户端提供时间范围
0 _* Y: w, A2 n* Q4 y1.不考虑TrueTime
2 g* G1 C4 Y" @4 \3 L! X& D首先,不考虑TrueTime的影响,也就是说,假设TrueTime API获得的时间是精确
5 m; }4 V2 Z7 Q6 j的。如果事务读写的数据只属于同一个Paxos组,那么,每个读写事务的执行步骤如$ {* _. ?0 x* U7 @
下: r# [- f; e9 _5 Y( Y7 E& F
1)获取系统的当前时间戳;
& u5 h# g) C+ C5 f9 S3 V0 a& ?2)执行读写操作,并将第1步取得的时间戳作为事务的提交版本。: Z* S$ R) k) { q
每个只读事务的执行步骤如下:
7 f. v4 P3 I4 _0 I+ [1)获取系统的当前时间戳,作为读事务的版本;
% v0 i' j3 f. r, f' |0 ]! i2)执行读取操作,返回客户端所有提交版本小于读事务版本的事务操作结果。: K; ?: {* a( F' h, L- i
快照读和只读事务的区别在于:快照读将指定读事务的版本,而不是取系统的
+ Y, G8 f' j: n; |, ]. S1 D4 a当前时间戳。
6 }2 W, p3 F# \# x4 |( n) {如果事务读写的数据涉及多个Paxos组,那么,对于读写事务,需要执行一次两
! y; Q9 n9 C! {* b: Z- O" i阶段提交协议,执行步骤如下:. G, l* p! X% E8 z3 U- x
1)Prepare:客户端将数据发往多个Paxos组的主副本,同时,协调者主副本发
4 R+ c$ i" g7 s" _5 }起prepare协议,请求其他的参与者主副本锁住需要操作的数据。
2 C# u: X' y) v4 y- T6 J/ U7 ~% C+ S. T2)Commit:协调者主副本发起Commit协议,要求每个参与者主副本执行提交操3 n/ w' b% T* M- s% w9 {$ S6 }3 U
作并解除Prepare阶段锁定的数据。协调者主副本可以将它的当前时间戳作为该事务
% _) n( m, }( O, Y. m Q2 e" G的提交版本,并发送给每个参与者主副本。
' l) B/ X' k. [6 d$ F7 k只读事务读取每个Paxos组中提交版本小于读事务版本的事务操作结果。需要注
: D: { N7 P; I意的是,只读事务需要保证不会读到不完整的事务。假设有一个读写事务修改了两
; W" @0 c9 W# j5 D个Paxos组:Paxos组A和Paxos组B,Paxos组A上的修改已提交,Paxos组B上的修改还未
8 l$ o' g+ L2 _7 W7 ~, o( N9 o提交。那么,只读事务会发现Paxos组B处于两阶段提交协议中的Prepare阶段,需要
# ?5 Q1 ~$ q" d" `; G; U3 Y7 n等待一会,直到Paxos组B上的修改生效后才能读到正确的数据。- F" _7 `* g- t. u+ D5 S
2.考虑TrueTime
" u \7 F- q0 @( B& i: z1 v如果考虑TrueTime,并发控制变得复杂。这里的核心思想在于,只要事务T1的) t0 `8 k+ t( @6 v7 e. p( _3 }
提交操作早于事务T2的开始操作,即使考虑TrueTime API的误差因素(-e到+e之间,
0 b _4 N! c8 `: O( V1 P2 b2 Ze值平均为4ms),Spanner也能保证事务T1的提交版本小于事务T2的提交版本。! V9 f- F: R* W2 l& c! w; Q; m8 |
Spanner使用了一种称为延迟提交(Commit Wait)的手段,即如果事务T1的提交版本
. Y0 J+ v/ y; C3 V5 s为时间戳t commit ,那么,事务T1会在t commit +e之后才能提交。另外,如果事务T2开始
, {0 z" m' b% G) {5 V的绝对时间为t abs ,那么事务T2的提交版本至少为t abs +e。这样,就保证了事务T1和T2/ ]: G L0 c: P5 O
之间严格的顺序,当然,这也意味着每个写事务的延时至少为2e。从这一点也可以
) e* J j5 |; }* {2 F3 k看出,Spanner实现功能完备的全球数据库是付出了一定代价的,设计架构时不能盲
3 z& K" V+ s/ F% Y( j目崇拜。& c5 k1 \9 M4 P0 c% j5 Y! ~+ _# [7 D
7.3.6 数据迁移! ~0 @. R+ L) Z* u3 T) i7 ]: e
目录是Spanner中对数据分区、复制和迁移的基本单位,用户可以指定一个目录; h& K1 r6 L6 f- Q+ g c1 g* _+ o
有多少个副本,分别存放在哪些机房中,例如将用户的目录存放在这个用户所在地2 h) I( I3 S" A& @
区附近的几个机房中。$ o' u( w6 [1 a0 ^: ]3 g/ k
一个Paxos组包含多个目录,目录可以在Paxos组之间移动。Spanner移动一个目' G) \+ V) T, T5 S) g% [
录一般出于以下几种考虑:0 e0 T7 X" H+ X
●某个Paxos组的负载太大,需要切分;
3 x1 Y9 k! ? z: A: Q4 @* o- S●将数据移动到离用户更近的地方,减少访问延时;/ a& {; }. J, A+ j1 O. h
●把经常一起访问的目录放进同一个Paxos组。
" R: d0 Y" G8 r. {1 v移动目录的操作在后台进行,不影响前台的客户端读写操作。一般来说,移动/ _7 G4 V! Q5 f: Z0 Y( G
一个50MB的目录大约只需要几秒钟时间。实现时,首先将目录的实际数据移动到指' ]1 u: Q4 L' n, g* X
定位置,然后再用一个原子操作更新元数据,从而完成整个移动过程。! u) a* q6 U, g0 d
7.3.7 讨论
6 l$ W4 D3 F8 RGoogle的分布式存储系统一步步地从Bigtable到Megastore,再到Spanner,这也印0 ~) H6 N8 _" M( T
证了分布式技术和传统关系数据库技术融合的必然性,即底层通过分布式技术实现
( p n( R# A; @; A( S3 N& b) n可扩展性,上层通过关系数据库的模型和接口将系统的功能暴露给用户。5 u7 u& V: b# o1 S W7 l8 ?- V
阿里巴巴的OceanBase系统在设计之初就考虑到这两种技术融合的必然性,因
. n" {: q5 u; C8 G此,一开始就将系统的最终目标定为:可扩展的关系数据库。目前,OceanBase已经
0 g1 a/ P) {1 l ^( i" o! r/ B& w8 K开发完成了部分功能并在阿里巴巴各个子公司获得广泛的应用。本书第三篇将详细
! R2 P6 C# F( N: L2 N介绍OceanBase的技术细节。
- ?4 t7 H! Y) |* B2 U, _7 l9 I, _5 Y U5 i+ E! R; Q
: n8 K/ H, c. A" `3 ?! i+ b& q/ F |
|