|
7.3 Google Spanner2 Z6 {* Q' t# L" l. N# w
Google Spanner是Google的全球级分布式数据库(Globally-Distributed
$ d/ N6 K% V! j0 `- G. E7 A ?Database)。Spanner的扩展性达到了全球级,可以扩展到数百个数据中心,数百万5 s5 J. K; S% X0 q5 M/ ]( m. t0 l( J
台机器,上万亿行记录。更为重要的是,除了夸张的可扩展性之外,它还能通过同8 t; f7 l/ i9 ~7 C
步复制和多版本控制来满足外部一致性,支持跨数据中心事务。
3 [, Z+ ]; Z/ a& \% B无论从学术研究还是工程实践的角度看,Spanner都是一个划时代的分布式存储6 A6 G/ U0 b9 G8 l9 r
系统。Spanner的成功说明了一点,分布式技术能够和数据库技术有机地结合起来,
1 U n+ h7 C+ M4 w* X+ z通过分布式技术实现高可扩展性,并呈现给使用者类似关系数据库的数据模型。# s" i0 {9 p& G0 [/ X& B
7.3.1 数据模型
0 V0 v5 y7 T" n' e$ d# G* dSpanner的数据模型与6.2节中介绍的Megastore系统比较类似。 S9 M; a* a9 n
如图7-6所示,对于一个典型的相册应用,需要存储其用户和相册,可以用上面
# g" D4 }* T6 [的两个SQL语句来创建表。Spanner的表是层次化的,最底层的表是目录表
8 `4 |& W. l& ^3 W+ u- r# ](Directorytable),其他表创建时,可以用INTERLEAVE IN PARENT来表示层次关
# E9 w" m% D6 \; N系。Spanner中的目录相当于Megastore中的实体组,一个用户的信息(user_id,email)3 t: ?- O9 _) f$ i8 B& k! c1 p
以及这个用户下的所有相片信息构成一个目录。实际存储时,Spanner会将同一个目# e S( S% D! d G) Z$ I
录的数据存放到一起,只要目录不太大,同一个目录的每个副本都会分配到同一台. Y3 I7 f+ v5 z/ b- W$ A" Q
机器。因此,针对同一个目录的读写事务大部分情况下都不会涉及跨机操作。+ A: E0 x" E* S- w+ \+ ]: p$ [
图 7-6 Spanner数据模型
/ D. l/ M' K) t! m# x% T! M7.3.2 架构) |' d7 C2 |6 F/ @; e% H8 x, H
Spanner构建在Google下一代分布式文件系统Colossus之上。Colossus是GFS的延 S8 P# y/ @. i) S+ Z' r6 w
续,相比GFS,Colossus的主要改进点在于实时性,并且支持海量小文件。, B$ w4 l4 z9 W, r/ f, \0 l
由于Spanner是全球性的,因此它有两个其他分布式存储系统没有的概念:
1 k& g- E3 c3 C0 r+ h●Universe。一个Spanner部署实例称为一个Universe。目前全世界有3个,一个开
Z7 n9 s* b. J7 b- |7 U; U发、一个测试、一个线上。Universe支持多数据中心部署,且多个业务可以共享同一
! \; ]. m) [: a) s5 P6 E/ a \个Universe。$ b7 |; I7 n+ U) _& _9 |
●Zones。每个Zone属于一个数据中心,而一个数据中心可能有多个Zone。一般
$ K8 n M E+ f( c4 H7 C/ J+ G$ T来说,Zone内部的网络通信代价较低,而Zone与Zone之间通信代价很高。) t4 g% C; L% E' G5 c" W5 t
如图7-7所示,Spanner系统包含如下组件:
0 }5 p$ y7 s6 m图 7-7 Spanner整体架构
1 O& S$ v" X8 M$ q' [2 c4 K●Universe Master:监控这个Universe里Zone级别的状态信息。
0 V) h1 O8 h5 |0 Q5 A2 u4 F" P●Placement Driver:提供跨Zone数据迁移功能。
, F- M$ C2 q7 z# v% _6 S$ X●Location Proxy:提供获取数据的位置信息服务。客户端需要通过它才能够知道
) O% `0 k3 B$ f2 z+ d3 N, r- t数据由哪台Spanserver服务。
5 Q3 s+ \& @( V2 p9 d6 F h●Spanserver:提供存储服务,功能上相当于Bigtable系统中的Tablet Server。
! z& j' i' f! I2 a P! C1 M每个Spanserver会服务多个子表,而每个子表又包含多个目录。客户端往Spanner
8 y# P1 k" J0 {; w( D9 Z& V5 }- C6 z6 ?发送读写请求时,首先查找目录所在的Spanserver,接着从Spanserver读写数据。
+ k4 u+ j. [. u( }* w+ {1 R+ i- W这里面有一个问题:如何存储目录与Spanserver之间的映射关系?假设每个用户, y3 z$ j, w8 s* X7 q8 ^
对应一个目录,全球总共有50亿用户,那么,映射关系的数据规模为几十亿到几百$ M% T$ s' ]8 d( V- Z( p- F5 x0 _
亿,单台服务器无法存放。Spanner论文中没有明确说明,笔者猜测这里的做法和
7 H# M! C& m$ t3 \: S+ ~, O3 ^. {# cBigtable类似,即将映射关系这样的元数据信息当成元数据表格,和普通用户表格采
) W5 p+ W! k: z; L, g5 z5 W取相同的存储方式。3 ^+ k% u7 N! u4 T5 g* w$ V
7.3.3 复制与一致性
: d2 a7 {: [; U0 ?% \8 G如图7-8所示,每个数据中心运行着一套Colossus,每个机器有100~1000个子0 Z+ D& M+ u# O$ b+ B2 n
表,每个子表会在多个数据中心部署多个副本。为了同步系统中的操作日志,每个9 O+ ^ l* {2 k1 H
子表上会运行一个Paxos状态机。Paxos协议会选出一个副本作为主副本,这个主副本: P0 W2 I- Q( S0 [) a$ h. [) l
的寿命默认是10秒。正常情况下,这个主副本会在快要到期的时候将自己再次选为
! _5 n/ p# z( L& }2 `2 v2 j主副本;如果出现异常,例如主副本所在的spanserver宕机,其他副本会在10秒后通
6 \# h Z" u; s过Paxos协议选举为新的主副本。: Q" m; x' R( K# z2 j; z9 K
图 7-8 Spanner多集群复制
; _6 A2 |* e5 l9 I' p通过Paxos协议,实现了跨数据中心的多个副本之间的一致性。另外,每个主副
5 ?- g, l0 a8 u本所在的Spanserver还会实现一个锁表用于并发控制,读写事务操作某个子表上的目1 m; N! F% U! f/ t5 f
录时需要通过锁表避免多个事务之间互相干扰。 d+ Q2 C$ L( g5 L' g5 {' M
除了锁表,每个主副本上还有一个事务管理器。如果事务在一个Paxos组里面,4 S/ y" p2 R0 O$ {& g
可以绕过事务管理器。但是一旦事务跨多个Paxos组,就需要事务管理器来协调。
4 Y! t0 j9 Q1 o锁表实现单个Paxos组内的单机事务,事务管理器实现跨多个Paxos组的分布式事9 L- j) W" C" N6 Y. Q- M
务。为了实现分布式事务,需要实现3.7.1节中提到的两阶段提交协议。有一个Paxos
/ P0 z5 a* Y% F) K: ?组的主副本会成为两阶段提交协议中的协调者,其他Paxos组的主副本为参与者。 W/ T- F$ G J( W9 v1 s
7.3.4 TrueTime: P0 P- Z/ L, P% P" G/ h% i
为了实现并发控制,数据库需要给每个事务分配全局唯一的事务id。然而,在分) Y- D! M0 d$ \1 N5 K" W! l: h$ z- [
布式系统中,很难生成全局唯一id。一种方式是采用Google Percolator(Google
# }: g# g* u0 bCaffeine的底层存储系统)中的做法,即专门部署一套Oracle数据库用于生成全局唯( Z9 a* N# c. e) \
一id。虽然Oracle逻辑上是一个单点,但是实现的功能单一,因而能够做得很高效。- g& \! \& \3 |9 D) B
Spanner选择了另外一种做法,即全球时钟同步机制TrueTime。
2 r$ ]: N" @! d6 ^3 v& A0 ?TrueTime是一个提供本地时间的接口,但与Linux上的gettimeofday接口不一样的
& V8 t) r: r2 w5 y8 M( g是,它除了可以返回一个时间戳t,还会给出一个误差e。例如,返回的时间戳是20点2 d: K9 \# e% Q- N- z! j/ |3 \
23分30秒100毫秒,而误差是5毫秒,那么真实的时间在20点23分30秒95毫秒到105毫, j* F" Q$ x+ P" r4 Y( \8 M
秒之间。真实的系统e平均下来只有4毫秒。
( E* R* t- X+ a( P; cTrueTime API实现的基础是GPS和原子钟。之所以要用两种技术来处理,是因为# ` a4 u: ~5 Z0 y) u
导致这两种技术失效的原因是不同的。GPS会有一个天线,电波干扰会导致其失灵。- h4 M' w, \, ~# ?4 Y- q5 v
原子钟很稳定。当GPS失灵的时候,原子钟仍然能保证在相当长的时间内,不会出现& M' U" \/ u% ^4 a; A
偏差。
( H/ S" u) @; L( k- L每个数据中心需要部署一些主时钟服务器(Master),其他机器上部署一个从时8 Q9 D6 l- r3 @# z' {
钟进程(Slave)来从主时钟服务器同步时钟信息。有的主时钟服务器用GPS,有的2 ^. \, h% }6 k1 u
主时钟服务器用原子钟。每个从时钟进程每隔30秒会从若干个主时钟服务器同步时$ L! r7 V6 w" ], F% V
钟信息。主时钟服务器自己还会将最新的时间信息和本地时钟比对,排除掉偏差比
- ]$ _+ \& \: X0 c4 \较大的结果。
: p/ I% c5 g9 p$ j/ X j5 G7.3.5 并发控制. o+ C9 ? |2 y6 V) F$ z J, H
Spanner使用TrueTime来控制并发,实现外部一致性,支持以下几种事务:3 A/ j. E- E+ ?6 c b g
●读写事务
* @- k p7 ?' D' B●只读事务# w3 _4 R5 c- _! s
●快照读,客户端提供时间戳: O/ {1 Z) o2 G
●快照读,客户端提供时间范围
7 U4 k" ], }) ~7 h8 d1.不考虑TrueTime
# u8 h/ K! V& Q# E* D# b; ^首先,不考虑TrueTime的影响,也就是说,假设TrueTime API获得的时间是精确
, O3 H4 Q0 i; \' d4 y的。如果事务读写的数据只属于同一个Paxos组,那么,每个读写事务的执行步骤如7 N9 N% v$ k1 h4 r) h3 F
下:3 x9 |/ E% `' A |1 s
1)获取系统的当前时间戳;, F1 L" L3 U, z6 o- I2 _( T/ P
2)执行读写操作,并将第1步取得的时间戳作为事务的提交版本。/ T. S; ]; r% m7 a3 E
每个只读事务的执行步骤如下: j, h4 G0 B: J
1)获取系统的当前时间戳,作为读事务的版本;1 g+ Y' y( q0 c% {' U: [
2)执行读取操作,返回客户端所有提交版本小于读事务版本的事务操作结果。, l/ y5 ~2 ?- V& n+ t/ ^8 e& m2 ]
快照读和只读事务的区别在于:快照读将指定读事务的版本,而不是取系统的
% R" f+ ?* ~" R& S当前时间戳。
8 t3 p" K# s$ p/ V5 o如果事务读写的数据涉及多个Paxos组,那么,对于读写事务,需要执行一次两: D7 ?8 _9 Q7 j+ u
阶段提交协议,执行步骤如下:( W6 w2 V# j# R7 e& C* C, r
1)Prepare:客户端将数据发往多个Paxos组的主副本,同时,协调者主副本发
+ V [* T4 v) k D ]起prepare协议,请求其他的参与者主副本锁住需要操作的数据。9 \. m7 Z1 p6 K$ m4 L+ o5 g
2)Commit:协调者主副本发起Commit协议,要求每个参与者主副本执行提交操' m8 s8 X% E* e+ U# A$ J
作并解除Prepare阶段锁定的数据。协调者主副本可以将它的当前时间戳作为该事务
& u5 H4 k( K8 s' `2 J2 K9 g的提交版本,并发送给每个参与者主副本。4 Y7 u/ k6 o8 K
只读事务读取每个Paxos组中提交版本小于读事务版本的事务操作结果。需要注2 |4 a' z* { ]. A7 ^
意的是,只读事务需要保证不会读到不完整的事务。假设有一个读写事务修改了两- U8 F; P7 [3 F( L6 O
个Paxos组:Paxos组A和Paxos组B,Paxos组A上的修改已提交,Paxos组B上的修改还未
; T& [- q& P( E$ l3 P* s# ^提交。那么,只读事务会发现Paxos组B处于两阶段提交协议中的Prepare阶段,需要
# H. c3 U& f2 Z2 W等待一会,直到Paxos组B上的修改生效后才能读到正确的数据。
9 j' N1 U3 q) G, e. N9 O D# J2.考虑TrueTime
, Z k$ q5 x' C3 U& q8 X. D! h, J如果考虑TrueTime,并发控制变得复杂。这里的核心思想在于,只要事务T1的
" Y0 W+ v# |- b提交操作早于事务T2的开始操作,即使考虑TrueTime API的误差因素(-e到+e之间,* }. d$ J% ~1 v( x/ a; p0 C
e值平均为4ms),Spanner也能保证事务T1的提交版本小于事务T2的提交版本。
5 M# w- j! n& w2 u+ G1 \6 f2 \! T; pSpanner使用了一种称为延迟提交(Commit Wait)的手段,即如果事务T1的提交版本
5 D! f6 T! t- l1 e/ a% u为时间戳t commit ,那么,事务T1会在t commit +e之后才能提交。另外,如果事务T2开始
& `& H6 i6 _5 a2 |) ~# j( `& d的绝对时间为t abs ,那么事务T2的提交版本至少为t abs +e。这样,就保证了事务T1和T24 p L4 R" T2 C0 `4 M
之间严格的顺序,当然,这也意味着每个写事务的延时至少为2e。从这一点也可以5 _6 c( ], v# l& z2 I @$ P
看出,Spanner实现功能完备的全球数据库是付出了一定代价的,设计架构时不能盲
9 j. U: d" b8 A% O% d目崇拜。
* D$ {! { N; m7.3.6 数据迁移* Q0 Z. m5 u/ R% ]" `
目录是Spanner中对数据分区、复制和迁移的基本单位,用户可以指定一个目录
/ K/ w( U$ `& k2 h8 c0 }7 o4 r有多少个副本,分别存放在哪些机房中,例如将用户的目录存放在这个用户所在地5 X( ]- [7 Y. t) U: r' k0 L. r9 x
区附近的几个机房中。! d7 }. [4 |- j3 F6 \* H5 ?
一个Paxos组包含多个目录,目录可以在Paxos组之间移动。Spanner移动一个目
2 b! k$ Z8 e8 Y0 l- n7 _/ {7 i录一般出于以下几种考虑:
7 w8 M7 Y ~, e; \7 `5 O●某个Paxos组的负载太大,需要切分;0 j, M F1 _; s/ I
●将数据移动到离用户更近的地方,减少访问延时;
5 g! [+ u% h3 u( D●把经常一起访问的目录放进同一个Paxos组。
$ W/ N/ }0 U* v8 @1 d7 S移动目录的操作在后台进行,不影响前台的客户端读写操作。一般来说,移动. T: q/ r# V) y$ a3 q M- e
一个50MB的目录大约只需要几秒钟时间。实现时,首先将目录的实际数据移动到指1 m) K, h' Q( a2 p5 |% v) @
定位置,然后再用一个原子操作更新元数据,从而完成整个移动过程。
! t! {( k. g& q7.3.7 讨论$ S9 F3 ?% @% r8 p( J r$ u% t
Google的分布式存储系统一步步地从Bigtable到Megastore,再到Spanner,这也印
* |% g: _+ I$ m5 H0 J证了分布式技术和传统关系数据库技术融合的必然性,即底层通过分布式技术实现8 M) R# ^) l" n
可扩展性,上层通过关系数据库的模型和接口将系统的功能暴露给用户。
4 M$ B. v4 g# y5 E) a' Z { l阿里巴巴的OceanBase系统在设计之初就考虑到这两种技术融合的必然性,因
2 {$ y, ?4 ~) F( }$ z4 D+ M- |此,一开始就将系统的最终目标定为:可扩展的关系数据库。目前,OceanBase已经% M0 n% n7 d$ Q) d4 I8 ~+ {' f
开发完成了部分功能并在阿里巴巴各个子公司获得广泛的应用。本书第三篇将详细' R; l- C9 a! S9 Q
介绍OceanBase的技术细节。
: e5 U: r Z# Q' B- P6 T; i1 D/ {) g3 j; X- D" n/ s
3 Z" ^9 Q7 ~$ k# p- H
|
|