|
7.3 Google Spanner
. v A+ L+ O+ T+ O# i4 m h/ K1 i. }: A3 }Google Spanner是Google的全球级分布式数据库(Globally-Distributed9 ?# u. p% V6 V0 \5 J/ @7 ]
Database)。Spanner的扩展性达到了全球级,可以扩展到数百个数据中心,数百万
0 e7 W: F% b" j6 A! k& ?# o0 q0 C台机器,上万亿行记录。更为重要的是,除了夸张的可扩展性之外,它还能通过同
! v! P. [1 u4 F1 [5 m9 S步复制和多版本控制来满足外部一致性,支持跨数据中心事务。
& f& P! L' G, ~" }5 B! d无论从学术研究还是工程实践的角度看,Spanner都是一个划时代的分布式存储
' S) g& V% E$ R* t* k系统。Spanner的成功说明了一点,分布式技术能够和数据库技术有机地结合起来,
. |: ^/ R3 D" m3 z3 |) R通过分布式技术实现高可扩展性,并呈现给使用者类似关系数据库的数据模型。
. D& m# q$ B( ^; S& |7.3.1 数据模型" U7 [8 d$ Q" I# s
Spanner的数据模型与6.2节中介绍的Megastore系统比较类似。
1 h, ?7 t: G# `5 b+ s3 {; w6 E如图7-6所示,对于一个典型的相册应用,需要存储其用户和相册,可以用上面
" }* J- `( V2 m0 p1 @的两个SQL语句来创建表。Spanner的表是层次化的,最底层的表是目录表% ?1 d7 W Z3 D2 C, ^
(Directorytable),其他表创建时,可以用INTERLEAVE IN PARENT来表示层次关
" I# K5 C) V) O/ T: B系。Spanner中的目录相当于Megastore中的实体组,一个用户的信息(user_id,email)9 O1 z. C1 M/ s2 |8 s/ g
以及这个用户下的所有相片信息构成一个目录。实际存储时,Spanner会将同一个目
2 a5 W6 i- F& J) T3 E' s录的数据存放到一起,只要目录不太大,同一个目录的每个副本都会分配到同一台
% S. [/ G# @8 }机器。因此,针对同一个目录的读写事务大部分情况下都不会涉及跨机操作。
; {: c4 ]3 U3 f8 S/ w图 7-6 Spanner数据模型( U/ [. U' p: [: }) \+ w
7.3.2 架构3 r& U+ \2 S2 Q9 |2 J/ L
Spanner构建在Google下一代分布式文件系统Colossus之上。Colossus是GFS的延
- r) R# p0 ^7 S' B; \续,相比GFS,Colossus的主要改进点在于实时性,并且支持海量小文件。
3 V' T+ v- v3 Y" `' V& D/ S4 Y; ^由于Spanner是全球性的,因此它有两个其他分布式存储系统没有的概念:; o; w, U) G% J# {) L5 q: @1 p( Q
●Universe。一个Spanner部署实例称为一个Universe。目前全世界有3个,一个开4 K: ^8 V" S$ B# _
发、一个测试、一个线上。Universe支持多数据中心部署,且多个业务可以共享同一; @& f' X i# B% E+ L: F) o, ~! K
个Universe。
1 { R1 W7 K: ` j( K●Zones。每个Zone属于一个数据中心,而一个数据中心可能有多个Zone。一般
* H; I/ n. O! \ K! d2 c! T& @. N来说,Zone内部的网络通信代价较低,而Zone与Zone之间通信代价很高。
8 r! |" F6 q& P/ T3 e, }如图7-7所示,Spanner系统包含如下组件:
5 b0 B' W) A7 ]% {" G6 }图 7-7 Spanner整体架构% V; y+ i. D/ H: c* U3 C: y/ s
●Universe Master:监控这个Universe里Zone级别的状态信息。& {$ f# t W/ Z1 n$ V
●Placement Driver:提供跨Zone数据迁移功能。
1 c7 a( G6 U- N5 `7 ^/ D6 ^# k' o●Location Proxy:提供获取数据的位置信息服务。客户端需要通过它才能够知道
5 z8 g' x: W4 T数据由哪台Spanserver服务。
2 X- _' g% ]4 O1 O( ^●Spanserver:提供存储服务,功能上相当于Bigtable系统中的Tablet Server。4 H" E9 X( x8 t. c/ X1 v
每个Spanserver会服务多个子表,而每个子表又包含多个目录。客户端往Spanner# e* E" R) c3 g
发送读写请求时,首先查找目录所在的Spanserver,接着从Spanserver读写数据。6 M: Q5 J3 \' w8 U* `) Z0 U
这里面有一个问题:如何存储目录与Spanserver之间的映射关系?假设每个用户& c, N- Y) z4 q" e
对应一个目录,全球总共有50亿用户,那么,映射关系的数据规模为几十亿到几百! b' f- P( u# h& w# p7 V
亿,单台服务器无法存放。Spanner论文中没有明确说明,笔者猜测这里的做法和: T8 K+ E' E) |3 v
Bigtable类似,即将映射关系这样的元数据信息当成元数据表格,和普通用户表格采- M9 O! V9 q7 {/ _
取相同的存储方式。 A% W0 c: x# r' Z+ G" h3 l. U
7.3.3 复制与一致性0 G* Q" P6 f+ y! i
如图7-8所示,每个数据中心运行着一套Colossus,每个机器有100~1000个子% B' u5 E0 y: Q2 Y0 R8 H
表,每个子表会在多个数据中心部署多个副本。为了同步系统中的操作日志,每个% k9 q; B4 O/ c& l
子表上会运行一个Paxos状态机。Paxos协议会选出一个副本作为主副本,这个主副本
% O5 n' ]1 U+ A& m! {的寿命默认是10秒。正常情况下,这个主副本会在快要到期的时候将自己再次选为; n# z; I, m# g% R
主副本;如果出现异常,例如主副本所在的spanserver宕机,其他副本会在10秒后通: Y' @" `7 T+ Y( B3 R7 _
过Paxos协议选举为新的主副本。
1 t5 s1 v. f! J. F. Y图 7-8 Spanner多集群复制) Z# M) X; T) h. N0 `9 ?! f
通过Paxos协议,实现了跨数据中心的多个副本之间的一致性。另外,每个主副
- }( s: ]2 U4 q1 _4 I% ^本所在的Spanserver还会实现一个锁表用于并发控制,读写事务操作某个子表上的目7 c3 [, w" _; ]: H' v" n; l( X
录时需要通过锁表避免多个事务之间互相干扰。6 }9 r. I2 K, C# U
除了锁表,每个主副本上还有一个事务管理器。如果事务在一个Paxos组里面,: o% u }3 L/ ]5 o4 N. o9 w; [
可以绕过事务管理器。但是一旦事务跨多个Paxos组,就需要事务管理器来协调。
. `/ h1 w- Q+ z, s$ N锁表实现单个Paxos组内的单机事务,事务管理器实现跨多个Paxos组的分布式事( K: }( |# S$ m% u2 K
务。为了实现分布式事务,需要实现3.7.1节中提到的两阶段提交协议。有一个Paxos' F6 H0 u0 D% k8 o6 F! J" M. c! e
组的主副本会成为两阶段提交协议中的协调者,其他Paxos组的主副本为参与者。4 ^, M. i& N; _' O# J
7.3.4 TrueTime M- Q, W) s+ ^
为了实现并发控制,数据库需要给每个事务分配全局唯一的事务id。然而,在分" M5 j. S; i) [2 Z/ W
布式系统中,很难生成全局唯一id。一种方式是采用Google Percolator(Google. k# `! z- }9 T# W
Caffeine的底层存储系统)中的做法,即专门部署一套Oracle数据库用于生成全局唯
: H( t, Q& z$ J7 w/ H一id。虽然Oracle逻辑上是一个单点,但是实现的功能单一,因而能够做得很高效。2 H; ?1 G+ r7 E- F% H v( M
Spanner选择了另外一种做法,即全球时钟同步机制TrueTime。( o, I; ~& i2 j, m7 @4 `+ O0 v M
TrueTime是一个提供本地时间的接口,但与Linux上的gettimeofday接口不一样的
. v. D" H; U- Q7 j是,它除了可以返回一个时间戳t,还会给出一个误差e。例如,返回的时间戳是20点
5 v2 s/ r% U$ t( ?: D3 H- @" p23分30秒100毫秒,而误差是5毫秒,那么真实的时间在20点23分30秒95毫秒到105毫
# `% C" j3 R) f! x% O* U2 A秒之间。真实的系统e平均下来只有4毫秒。
+ [: |6 s0 R: G6 k8 A( X8 dTrueTime API实现的基础是GPS和原子钟。之所以要用两种技术来处理,是因为
2 g. @9 }3 ^$ h, D% |. X6 {导致这两种技术失效的原因是不同的。GPS会有一个天线,电波干扰会导致其失灵。& y& H. P8 I a. c4 R% c4 G
原子钟很稳定。当GPS失灵的时候,原子钟仍然能保证在相当长的时间内,不会出现
6 T: R; _1 |+ M2 S* C& z* o偏差。' H2 v! w9 @( D: o! O
每个数据中心需要部署一些主时钟服务器(Master),其他机器上部署一个从时
5 b& z3 [8 d1 n+ b- g* d+ m钟进程(Slave)来从主时钟服务器同步时钟信息。有的主时钟服务器用GPS,有的
. \0 N1 M: i) o3 [1 i主时钟服务器用原子钟。每个从时钟进程每隔30秒会从若干个主时钟服务器同步时" |' r& A1 b1 e- T$ p& s1 |
钟信息。主时钟服务器自己还会将最新的时间信息和本地时钟比对,排除掉偏差比- a0 [+ D4 @ S7 e* q' z
较大的结果。" R- z& M! a1 W1 A. A( c- _
7.3.5 并发控制* f$ G6 p# k v2 `4 A
Spanner使用TrueTime来控制并发,实现外部一致性,支持以下几种事务:, K- ?+ h7 H$ ~, G( b2 {/ n' O1 C
●读写事务7 m7 ` \" R2 x
●只读事务3 X C9 t2 ?! Q
●快照读,客户端提供时间戳
; e* W. n8 f) P4 J: v0 g●快照读,客户端提供时间范围
9 ^/ [$ N8 v6 @" K+ A1.不考虑TrueTime0 r2 D4 d) N3 C: C0 P6 @- N5 w
首先,不考虑TrueTime的影响,也就是说,假设TrueTime API获得的时间是精确
S3 f9 m, w% }+ ~; U. ~8 w的。如果事务读写的数据只属于同一个Paxos组,那么,每个读写事务的执行步骤如! f7 n l7 R6 i& ^, }
下:+ Z% X6 V& r- J- a4 l; j
1)获取系统的当前时间戳;
: A) Q4 a, n* k: W, i* Y2)执行读写操作,并将第1步取得的时间戳作为事务的提交版本。- _. E* A; b. J& l+ E7 O
每个只读事务的执行步骤如下:. W ]% f. {1 H- w5 M
1)获取系统的当前时间戳,作为读事务的版本;' K0 c$ D' i0 W, m
2)执行读取操作,返回客户端所有提交版本小于读事务版本的事务操作结果。
% B9 t$ \) Z9 F$ E( ]" O! G5 z快照读和只读事务的区别在于:快照读将指定读事务的版本,而不是取系统的- I+ }$ z) e& {6 \
当前时间戳。
1 g' h4 ~% g/ u% y# ?如果事务读写的数据涉及多个Paxos组,那么,对于读写事务,需要执行一次两
- s* |6 U3 h3 X1 p7 r7 [/ r9 q阶段提交协议,执行步骤如下:# h9 w& A6 d6 I& R% N- C
1)Prepare:客户端将数据发往多个Paxos组的主副本,同时,协调者主副本发& W' m+ Q" P% M+ |
起prepare协议,请求其他的参与者主副本锁住需要操作的数据。
& y; B: A( s( r( z' t \2)Commit:协调者主副本发起Commit协议,要求每个参与者主副本执行提交操
$ Z' V, T# O. e5 ?5 E作并解除Prepare阶段锁定的数据。协调者主副本可以将它的当前时间戳作为该事务7 O3 N) `- b6 k2 n# w
的提交版本,并发送给每个参与者主副本。
- P5 d- g0 u6 L只读事务读取每个Paxos组中提交版本小于读事务版本的事务操作结果。需要注" M! [! k$ M0 ^: _
意的是,只读事务需要保证不会读到不完整的事务。假设有一个读写事务修改了两
6 v" `, d: K8 |! Q/ Z+ e/ v* r% f个Paxos组:Paxos组A和Paxos组B,Paxos组A上的修改已提交,Paxos组B上的修改还未1 r# h' e8 ~) O ^
提交。那么,只读事务会发现Paxos组B处于两阶段提交协议中的Prepare阶段,需要
9 I/ ~9 `- f$ x. c! C等待一会,直到Paxos组B上的修改生效后才能读到正确的数据。
" ]4 z- z# x9 ]2.考虑TrueTime
. C8 V2 N& c, V2 |; Z( b如果考虑TrueTime,并发控制变得复杂。这里的核心思想在于,只要事务T1的
/ ~ W8 [: H( \1 Q: U; J提交操作早于事务T2的开始操作,即使考虑TrueTime API的误差因素(-e到+e之间,$ e9 U' E+ e; Z; U/ J5 z- ~/ Q/ F9 h
e值平均为4ms),Spanner也能保证事务T1的提交版本小于事务T2的提交版本。
" t7 `4 c- T6 o" {6 o# ?/ R& cSpanner使用了一种称为延迟提交(Commit Wait)的手段,即如果事务T1的提交版本4 ]3 j" {( R& D( L8 [ K3 J; \
为时间戳t commit ,那么,事务T1会在t commit +e之后才能提交。另外,如果事务T2开始 h. L- w9 h! C
的绝对时间为t abs ,那么事务T2的提交版本至少为t abs +e。这样,就保证了事务T1和T29 [" y3 S4 o' ^+ v s% v
之间严格的顺序,当然,这也意味着每个写事务的延时至少为2e。从这一点也可以
( W5 q1 j9 z. I2 l0 i# f9 A% Q. W2 s$ @看出,Spanner实现功能完备的全球数据库是付出了一定代价的,设计架构时不能盲
( N) o, \9 M# z0 Z9 q& b, J: E目崇拜。
8 O# _# f$ }, \" Q7.3.6 数据迁移$ r; C1 Q* w6 S' V) h
目录是Spanner中对数据分区、复制和迁移的基本单位,用户可以指定一个目录
0 v5 N/ D' {* l有多少个副本,分别存放在哪些机房中,例如将用户的目录存放在这个用户所在地5 j6 g! c& o( R/ g f5 v
区附近的几个机房中。4 w* T7 t5 L/ X* y& a( X
一个Paxos组包含多个目录,目录可以在Paxos组之间移动。Spanner移动一个目
: B* a" m- [# q录一般出于以下几种考虑:
# D% T3 W( ^" t: I. N●某个Paxos组的负载太大,需要切分;
1 T- i) }4 L# _" M●将数据移动到离用户更近的地方,减少访问延时;$ H# b* T& x% j4 G3 {( i" j
●把经常一起访问的目录放进同一个Paxos组。+ k& Y, N! U; [8 n, o
移动目录的操作在后台进行,不影响前台的客户端读写操作。一般来说,移动6 Q4 Q6 W/ V. Z* u7 d5 w! v3 @
一个50MB的目录大约只需要几秒钟时间。实现时,首先将目录的实际数据移动到指
' Z Q' m4 W: C% z定位置,然后再用一个原子操作更新元数据,从而完成整个移动过程。
2 J, {& c: I# l+ ^$ D, z' d7.3.7 讨论
, R5 x; N! O' c8 VGoogle的分布式存储系统一步步地从Bigtable到Megastore,再到Spanner,这也印
* P( J; l2 p2 K证了分布式技术和传统关系数据库技术融合的必然性,即底层通过分布式技术实现
Y' [; O# Z; k0 s' X& }4 D& F; e2 I可扩展性,上层通过关系数据库的模型和接口将系统的功能暴露给用户。
5 C$ T+ {! j1 Z* U: P阿里巴巴的OceanBase系统在设计之初就考虑到这两种技术融合的必然性,因
" S% h& n; u) E4 L; Z此,一开始就将系统的最终目标定为:可扩展的关系数据库。目前,OceanBase已经! G6 O7 u6 w8 o" P4 P0 b$ X
开发完成了部分功能并在阿里巴巴各个子公司获得广泛的应用。本书第三篇将详细6 P: Z, `, |. ]# X
介绍OceanBase的技术细节。 G# [1 u7 ^9 k9 C! S1 a- E
! }# B$ ]- t3 c
8 U8 q* ]6 @- N O" B( K
|
|