|
7.3 Google Spanner: e8 n/ u2 }& k# m7 Z- X& a7 k5 t7 O. L
Google Spanner是Google的全球级分布式数据库(Globally-Distributed
2 A/ ]( a# |7 ?3 z4 L- FDatabase)。Spanner的扩展性达到了全球级,可以扩展到数百个数据中心,数百万7 W, p* G" {; p
台机器,上万亿行记录。更为重要的是,除了夸张的可扩展性之外,它还能通过同
7 @/ T" i4 A7 ] Y9 {! H2 d步复制和多版本控制来满足外部一致性,支持跨数据中心事务。
! ~- j. F7 j5 h% I/ _# W' v, n无论从学术研究还是工程实践的角度看,Spanner都是一个划时代的分布式存储9 a+ M0 ]9 z) g, i
系统。Spanner的成功说明了一点,分布式技术能够和数据库技术有机地结合起来," l/ `. A) \3 r H$ a9 y0 j. B
通过分布式技术实现高可扩展性,并呈现给使用者类似关系数据库的数据模型。
, \7 s) t' `* c! f+ R/ M7.3.1 数据模型7 v- V% P8 ^% e$ h
Spanner的数据模型与6.2节中介绍的Megastore系统比较类似。
" t5 U' i/ r9 @2 A5 y4 o6 B) p如图7-6所示,对于一个典型的相册应用,需要存储其用户和相册,可以用上面
8 T1 r0 V: W3 ]6 e* h& a% g1 s3 g的两个SQL语句来创建表。Spanner的表是层次化的,最底层的表是目录表1 P, k i& P0 U, T
(Directorytable),其他表创建时,可以用INTERLEAVE IN PARENT来表示层次关6 p' t. X2 ?; r% N
系。Spanner中的目录相当于Megastore中的实体组,一个用户的信息(user_id,email)
; p' V) {: Q5 O2 I* r6 l以及这个用户下的所有相片信息构成一个目录。实际存储时,Spanner会将同一个目) M1 C, t6 Q& h! F4 j2 d$ {6 w4 T% i
录的数据存放到一起,只要目录不太大,同一个目录的每个副本都会分配到同一台
* y) O: m) d, m7 v. A" H8 S机器。因此,针对同一个目录的读写事务大部分情况下都不会涉及跨机操作。; W9 G$ o: Z# N' {
图 7-6 Spanner数据模型* I, S% S. Y" d2 P: U* @1 B
7.3.2 架构
3 o4 L! k* w1 ~9 v: GSpanner构建在Google下一代分布式文件系统Colossus之上。Colossus是GFS的延
9 C2 q# s' e8 [" M- N续,相比GFS,Colossus的主要改进点在于实时性,并且支持海量小文件。
/ O( k: E% |$ `& u由于Spanner是全球性的,因此它有两个其他分布式存储系统没有的概念:
5 b1 O: r& O& K. ]* O●Universe。一个Spanner部署实例称为一个Universe。目前全世界有3个,一个开( A( E9 K! c+ {2 n
发、一个测试、一个线上。Universe支持多数据中心部署,且多个业务可以共享同一% L! D7 w# T9 {# P
个Universe。
3 a! S5 _4 x- s3 x- N$ H! D8 m" w●Zones。每个Zone属于一个数据中心,而一个数据中心可能有多个Zone。一般
1 G) t( z }6 Y来说,Zone内部的网络通信代价较低,而Zone与Zone之间通信代价很高。
0 I5 ?8 \/ K+ ~1 \# X2 v1 {/ v如图7-7所示,Spanner系统包含如下组件:" n( J! z/ s/ L) H( v
图 7-7 Spanner整体架构 Y! b7 p' r& w z
●Universe Master:监控这个Universe里Zone级别的状态信息。( F5 F2 h4 |% G) t3 ]
●Placement Driver:提供跨Zone数据迁移功能。" C3 i( @; h8 ?
●Location Proxy:提供获取数据的位置信息服务。客户端需要通过它才能够知道" p0 E$ H2 {# N1 p
数据由哪台Spanserver服务。
5 Y F# @8 H% E●Spanserver:提供存储服务,功能上相当于Bigtable系统中的Tablet Server。; M1 J. _) H: U8 }
每个Spanserver会服务多个子表,而每个子表又包含多个目录。客户端往Spanner7 ~, X& n7 h( C1 `( \1 d2 i
发送读写请求时,首先查找目录所在的Spanserver,接着从Spanserver读写数据。; U9 j2 H! H2 N- L2 I6 C3 Q
这里面有一个问题:如何存储目录与Spanserver之间的映射关系?假设每个用户
. y2 e- {+ o/ H) B" d1 W: ~) ?对应一个目录,全球总共有50亿用户,那么,映射关系的数据规模为几十亿到几百( k( n+ T5 b2 B' h$ M7 n, W$ j
亿,单台服务器无法存放。Spanner论文中没有明确说明,笔者猜测这里的做法和6 p( N1 Z1 P- x6 ]& U7 |9 S+ A
Bigtable类似,即将映射关系这样的元数据信息当成元数据表格,和普通用户表格采
1 p; o) T6 o' f, y$ P( [- d取相同的存储方式。. h; u9 b4 A% L' H3 P! R
7.3.3 复制与一致性
: @6 s! L1 @! }. a O如图7-8所示,每个数据中心运行着一套Colossus,每个机器有100~1000个子
- e% Z2 E$ q, V$ L4 d" W表,每个子表会在多个数据中心部署多个副本。为了同步系统中的操作日志,每个6 u! K* C# {4 U3 O& j
子表上会运行一个Paxos状态机。Paxos协议会选出一个副本作为主副本,这个主副本0 ] a/ b0 ~5 n) c
的寿命默认是10秒。正常情况下,这个主副本会在快要到期的时候将自己再次选为3 Z- m/ S2 f3 a4 j4 F
主副本;如果出现异常,例如主副本所在的spanserver宕机,其他副本会在10秒后通
4 G/ W( j# p p1 D- L' Z- a& C过Paxos协议选举为新的主副本。
+ K( t4 D: w" e+ M9 s图 7-8 Spanner多集群复制
% @9 d* A) [; C8 Y+ |2 Y0 U通过Paxos协议,实现了跨数据中心的多个副本之间的一致性。另外,每个主副
3 F g+ a7 C+ O本所在的Spanserver还会实现一个锁表用于并发控制,读写事务操作某个子表上的目
& u, j% M( p* C% Y+ c+ t Z录时需要通过锁表避免多个事务之间互相干扰。5 `3 A, C$ \& H7 G8 R4 q; C
除了锁表,每个主副本上还有一个事务管理器。如果事务在一个Paxos组里面,
( q2 x3 `$ b, f' N0 b( ~$ g可以绕过事务管理器。但是一旦事务跨多个Paxos组,就需要事务管理器来协调。
- Q7 s5 ~' c) }2 D7 ]8 ?% @锁表实现单个Paxos组内的单机事务,事务管理器实现跨多个Paxos组的分布式事
$ J: d2 d- y; p# @3 O务。为了实现分布式事务,需要实现3.7.1节中提到的两阶段提交协议。有一个Paxos
! y1 h1 N, r; k组的主副本会成为两阶段提交协议中的协调者,其他Paxos组的主副本为参与者。! t9 @( M+ \; O K! O x; S
7.3.4 TrueTime$ b0 D5 O2 [' c% M) b; R
为了实现并发控制,数据库需要给每个事务分配全局唯一的事务id。然而,在分5 l. s4 E! e5 H. f4 ?1 D5 K
布式系统中,很难生成全局唯一id。一种方式是采用Google Percolator(Google
3 } _) J1 f [0 ~Caffeine的底层存储系统)中的做法,即专门部署一套Oracle数据库用于生成全局唯4 Q A$ A: d. s6 y$ f: E2 z
一id。虽然Oracle逻辑上是一个单点,但是实现的功能单一,因而能够做得很高效。3 u& ^; b4 \5 _, s0 b" t
Spanner选择了另外一种做法,即全球时钟同步机制TrueTime。& }. S: u0 C7 S: f2 ^9 L F# d
TrueTime是一个提供本地时间的接口,但与Linux上的gettimeofday接口不一样的 I8 Y2 W% d$ M" _ w0 F0 }
是,它除了可以返回一个时间戳t,还会给出一个误差e。例如,返回的时间戳是20点# D5 @* P: | @2 K. k1 E
23分30秒100毫秒,而误差是5毫秒,那么真实的时间在20点23分30秒95毫秒到105毫' u _( q( H' e4 \0 F
秒之间。真实的系统e平均下来只有4毫秒。/ V; s* U5 u* y* A
TrueTime API实现的基础是GPS和原子钟。之所以要用两种技术来处理,是因为8 y) r4 J6 a0 e
导致这两种技术失效的原因是不同的。GPS会有一个天线,电波干扰会导致其失灵。9 g+ G& y/ i% c7 r) i/ T
原子钟很稳定。当GPS失灵的时候,原子钟仍然能保证在相当长的时间内,不会出现 ~. c7 l4 Y6 T Y2 O
偏差。# p3 Y: Z! o$ O
每个数据中心需要部署一些主时钟服务器(Master),其他机器上部署一个从时
, u% I! R5 Q" h' }钟进程(Slave)来从主时钟服务器同步时钟信息。有的主时钟服务器用GPS,有的
% ]$ V9 o6 |$ j! t' u主时钟服务器用原子钟。每个从时钟进程每隔30秒会从若干个主时钟服务器同步时* z1 U$ k8 G: W8 Z2 X$ h8 E
钟信息。主时钟服务器自己还会将最新的时间信息和本地时钟比对,排除掉偏差比
# t& ?: u \8 k" W' X }8 n) X4 i9 f较大的结果。3 r J' [( f- h: {
7.3.5 并发控制
) c( H5 V* D5 g) R4 z8 sSpanner使用TrueTime来控制并发,实现外部一致性,支持以下几种事务:
. ^. Y8 I! Y; Q% U8 r2 Q●读写事务* p) w B: w; l" k. C/ F6 r/ l* X
●只读事务
6 O+ c! O0 t, S! h! c! M) Q$ v●快照读,客户端提供时间戳
) L2 U, j! C2 K●快照读,客户端提供时间范围) S5 U' G* _% |% l, i. S5 j
1.不考虑TrueTime6 ]( D9 D* ?6 d: `0 K8 {. a
首先,不考虑TrueTime的影响,也就是说,假设TrueTime API获得的时间是精确
: S' L0 o. h: X+ Y# Z! u6 ^3 M的。如果事务读写的数据只属于同一个Paxos组,那么,每个读写事务的执行步骤如! x; U+ f ~2 J7 ?! w
下:
5 E7 w$ Q) p' v$ D, C k- e' a1)获取系统的当前时间戳;
) ]6 R" C7 n0 N6 K' @ z( U2 A% a" X2)执行读写操作,并将第1步取得的时间戳作为事务的提交版本。8 r* A1 m. ]. V) w% J3 Z
每个只读事务的执行步骤如下:
& }) g1 C8 S8 {6 }0 W; F6 G" }) E( |1)获取系统的当前时间戳,作为读事务的版本;% e8 U y: y& \% m
2)执行读取操作,返回客户端所有提交版本小于读事务版本的事务操作结果。0 e" J) n+ B! g4 T
快照读和只读事务的区别在于:快照读将指定读事务的版本,而不是取系统的& a9 w2 S& b# V$ f5 |( @$ h
当前时间戳。' E( U7 [8 t3 D
如果事务读写的数据涉及多个Paxos组,那么,对于读写事务,需要执行一次两! }+ W- p9 M, }8 Q# Q) P9 O
阶段提交协议,执行步骤如下:: @) X$ m2 j5 l5 V
1)Prepare:客户端将数据发往多个Paxos组的主副本,同时,协调者主副本发1 g4 V, }) q( P" q" P' g# M% M8 d
起prepare协议,请求其他的参与者主副本锁住需要操作的数据。# m4 Y- ^; T" e. g, D$ o
2)Commit:协调者主副本发起Commit协议,要求每个参与者主副本执行提交操2 G- V4 M o+ I( ^$ c
作并解除Prepare阶段锁定的数据。协调者主副本可以将它的当前时间戳作为该事务- i) y0 B5 N d7 V
的提交版本,并发送给每个参与者主副本。& U6 |" H% a9 I( n- j* t2 U5 O
只读事务读取每个Paxos组中提交版本小于读事务版本的事务操作结果。需要注& w/ s% }. U- L6 f" E7 ?* x- J+ G
意的是,只读事务需要保证不会读到不完整的事务。假设有一个读写事务修改了两
; T2 b, G# c" i9 J) e0 X) b; `8 d个Paxos组:Paxos组A和Paxos组B,Paxos组A上的修改已提交,Paxos组B上的修改还未) W: n& b F2 w2 C$ Z2 X1 Z f- s! L
提交。那么,只读事务会发现Paxos组B处于两阶段提交协议中的Prepare阶段,需要
& F3 N9 c1 U3 W% U等待一会,直到Paxos组B上的修改生效后才能读到正确的数据。
/ w6 J2 L e+ H! }) m! ? i/ w2.考虑TrueTime' n" l) V: S* J1 a5 G* T
如果考虑TrueTime,并发控制变得复杂。这里的核心思想在于,只要事务T1的
i: y$ i% ]! j$ A8 k1 I0 n提交操作早于事务T2的开始操作,即使考虑TrueTime API的误差因素(-e到+e之间,
& ~/ `6 d- b9 v" D) z7 M( _e值平均为4ms),Spanner也能保证事务T1的提交版本小于事务T2的提交版本。
7 Z2 }* R3 y A& f% @$ z: _Spanner使用了一种称为延迟提交(Commit Wait)的手段,即如果事务T1的提交版本/ q( V, q& c8 B& P$ \+ X
为时间戳t commit ,那么,事务T1会在t commit +e之后才能提交。另外,如果事务T2开始" N8 J( q- k6 q* T3 U
的绝对时间为t abs ,那么事务T2的提交版本至少为t abs +e。这样,就保证了事务T1和T20 A4 a2 k' F" d$ W/ F& O$ R; U
之间严格的顺序,当然,这也意味着每个写事务的延时至少为2e。从这一点也可以3 ]7 f- S8 g6 {2 Z9 q
看出,Spanner实现功能完备的全球数据库是付出了一定代价的,设计架构时不能盲# m. B8 a6 [2 R! {
目崇拜。0 m# P2 Y0 g' J( e/ c, y
7.3.6 数据迁移
. v! R6 Z: C% l( _+ R- V+ l目录是Spanner中对数据分区、复制和迁移的基本单位,用户可以指定一个目录" B5 i8 G1 R* A" h( F9 ^
有多少个副本,分别存放在哪些机房中,例如将用户的目录存放在这个用户所在地
3 k: M X. Y5 a, @* a6 e; X6 Q, H区附近的几个机房中。
! D5 R, g. E. O. S! c( j一个Paxos组包含多个目录,目录可以在Paxos组之间移动。Spanner移动一个目0 ^: ]$ m+ ~6 l9 E
录一般出于以下几种考虑:
# s4 G7 d- O, U2 J●某个Paxos组的负载太大,需要切分;
; R6 S- c' t( w% l●将数据移动到离用户更近的地方,减少访问延时;+ j$ W5 R- L, y3 u
●把经常一起访问的目录放进同一个Paxos组。- }1 ]$ x; U Z5 K
移动目录的操作在后台进行,不影响前台的客户端读写操作。一般来说,移动6 l. z. Q( o' J" w/ N; p% q9 Z
一个50MB的目录大约只需要几秒钟时间。实现时,首先将目录的实际数据移动到指
- Q6 ?" M. }( F定位置,然后再用一个原子操作更新元数据,从而完成整个移动过程。5 t5 Y. r' W( w9 F9 B; N- @
7.3.7 讨论8 [2 C* @' G. q
Google的分布式存储系统一步步地从Bigtable到Megastore,再到Spanner,这也印- N; O4 v. g" f; t
证了分布式技术和传统关系数据库技术融合的必然性,即底层通过分布式技术实现
+ e# i, B1 T( c5 K6 g; p可扩展性,上层通过关系数据库的模型和接口将系统的功能暴露给用户。
p, i4 K2 i- M- [阿里巴巴的OceanBase系统在设计之初就考虑到这两种技术融合的必然性,因" `2 _0 J5 {/ _& E6 f
此,一开始就将系统的最终目标定为:可扩展的关系数据库。目前,OceanBase已经; P/ C5 ^6 C* C" P/ F$ c' A% r$ M
开发完成了部分功能并在阿里巴巴各个子公司获得广泛的应用。本书第三篇将详细
& B9 Z# y% @# e- Z3 y介绍OceanBase的技术细节。
! W- l& [. A0 k2 E# V2 t* u7 |8 A1 J* W3 ]
/ c4 L! X: g& f# b+ X8 ~# a& Q |
|