|
第7章 分布式数据库: h- W2 u' h6 j+ B& E
关系数据库理论汇集了计算机科学家几十年的智慧,Oracle、Microsoft SQL
* ?& l% W. j! \, ^: X; Z4 sServer、MySQL等关系数据库系统广泛应用在各行各业中。可以说,没有关系数据
' P/ V1 k* o2 r6 }& t库,就没有今天的IT或者互联网行业。然而,关系数据库设计之初并没有预见到IT行
; @: J0 `0 I" n0 w6 J业发展如此之快,总是假设系统运行在单机这一封闭系统上。" `# r: C! f, }9 M; b) a! b$ j* @ c
有很多思路可以实现关系数据库的可扩展性。例如,在应用层划分数据,将不
@, g" S8 P% |同的数据分片划分到不同的关系数据库上,如MySQL Sharding;或者在关系数据库内
" U* Z6 ]! {( D C9 P部支持数据自动分片,如Microsoft SQL Azure;或者干脆从存储引擎开始重写一个全
0 T# k" p9 m6 d8 f' P新的分布式数据库,如Google Spanner以及Alibaba OceanBase。
3 p, P5 H6 n* W' u2 X本章首先介绍数据库中间层架构,接着介绍Microsoft SQL Azure,最后介绍' K7 d1 C; X" T( d# y, a
Google Spanner。3 v9 T; p( Y8 I$ C
7.1 数据库中间层
7 {. C8 d( u8 z |5 q为了扩展关系数据库,最简单也是最为常见的做法就是应用层按照规则将数据
$ u, m/ u9 o$ j2 @. J" K拆分为多个分片,分布到多个数据库节点,并引入一个中间层来对应用屏蔽后端的# d- k. i" h% [; V
数据库拆分细节。
( F1 G; n$ |6 f0 D8 D5 J4 Z0 R1 c7.1.1 架构
6 A M; d. i. D6 ]5 C' W: K2 q以MySQL Sharding架构为例,分为几个部分:中间层dbproxy集群、数据库组、% ^9 [( m2 F! [
元数据服务器、常驻进程,如图7-1所示。1 Y8 N" Y+ \8 C
图 7-1 数据库中间层架构8 r+ L" P, w! {& ?' J9 A
(1)MySQL客户端库
( F& M5 ^ Q' R5 B6 w应用程序通过MySQL原生的客户端与系统交互,支持JDBC,原有的单机访问数7 u# n. U: y( B! z4 M
据库程序可以无缝迁移。
' k4 W" g4 m7 D$ g3 E& d5 D(2)中间层dbproxy
% n A7 z2 ?' u6 S; w中间层解析客户端SQL请求并转发到后端的数据库。具体来讲,它解析MySQL% f- @6 R: @( e& i" Y
协议,执行SQL路由,SQL过滤,读写分离,结果归并,排序以及分组,等等。中间: E# ]1 i! a# D5 I1 @ _4 _. f9 E( }
层由多个无状态的dbproxy进程组成,不存在单点的情况。另外,可以在客户端与中3 \* D/ b4 _$ L2 H5 M1 l
间层之间引入LVS(Linux Virtual Server)对客户端请求进行负载均衡。需要注意的
+ ]1 {+ K u1 h# w6 X( c; C( _是,引入LVS后,客户端请求需要额外增加一层通信开销,因此,常见的做法是直接
3 z6 C# f( F9 T2 F5 [+ z4 V$ x8 X在客户端配置中间层服务器列表,由客户端处理请求负载均衡以及中间层服务器故
" F* q3 ^ A# S6 c' v障等情况。/ K. J6 E* p/ a0 l5 I! t
(3)数据库组dbgroup
6 x7 m' J2 I# t5 Z每个dbgroup由N台数据库机器组成,其中一台为主机(Master),另外N-1台为
% T3 b% _4 S4 \% W. w备机(Slave)。主机负责所有的写事务及强一致读事务,并将操作以binlog的形式复
, m/ J* \% I7 h, c3 q制到备机,备机可以支持有一定延迟的读事务。$ y7 [5 Q& ^5 e
(4)元数据服务器
j, \8 P. G8 s7 ^* P" U, {$ l元数据服务器主要负责维护dbgroup拆分规则并用于dbgroup选主。dbproxy通过元
# G" |5 O0 J( U9 @# e1 h数据服务器获取拆分规则从而确定SQL语句的执行计划。另外,如果dbgroup的主机1 n0 P8 d5 ~# T; O
出现故障,需要通过元数据服务器选主。元数据服务器本身也需要多个副本实现
0 N$ n+ K# G9 CHA,一种常见的方式是采用Zookeeper实现。$ O+ t( w5 T) o% ~
(5)常驻进程agents
: B+ g& x$ P n2 o4 ]部署在每台数据库服务器上的常驻进程,用于实现监控,单点切换,安装,卸
" s+ j& |% ]+ G; Q& I载程序等。dbgroup中的数据库需要进行主备切换,软件升级等,这些控制逻辑需要/ C+ z3 ~8 r; `* e) ?2 ]
与数据库读写事务处理逻辑隔离开来。* B: j: L4 n. R6 j
假设数据库按照用户哈希分区,同一个用户的数据分布在一个数据库组上。如, s/ `8 P3 p. j" h9 d
果SQL请求只涉及同一个用户(这对于大多数应用都是成立的),那么,中间层将请
' I) d; X+ G6 O# }+ x9 J$ E* s" g求转发给相应的数据库组,等待返回结果并将结果返回给客户端;如果SQL请求涉及
# z' R- t8 b) e0 R6 e多个用户,那么中间层需要转发给多个数据库组,等待返回结果并将结果执行合
# B6 h# n2 f/ K并、分组、排序等操作后返回客户端。由于中间层的协议与MySQL兼容,客户端完* F& D: P( N' X: t
全感受不到与访问单台MySQL机器之间的差别。: h4 s/ t! k r* A
7.1.2 扩容; W$ t1 @, O6 Z" }) O6 B8 m4 k
MySQL Sharding集群一般按照用户id进行哈希分区,这里面存在两个问题:
: `2 J( r) q) v) j" W4 r1)集群的容量不够怎么办?
8 m* ^3 g. b# u3 T' _. j9 U2)单个用户的数据量太大怎么办?- \" y" Z6 W% C
对于第1个问题,MySQL Sharding集群往往会采用双倍扩容的方案,即从2台服务/ L% g0 f$ g7 {4 u8 C( @% z
器扩到4台,接着再扩到8台……,依次类推。$ Y7 m1 {9 {' F! g3 B
假设原来有2个dbgroup,第一个dbgroup的主机为A0,备机为A1,第二个dbgroup
: g- \9 R! V7 i5 L. l( v的主机为B0,备机为B1。按照用户id哈希取模,结果为奇数的用户分布在第一个- I+ O* E- {4 S4 E, ?0 ]
dbgroup,结果为偶数的用户分布在第二个dbgroup。常见的一种扩容方式如下:1 g9 q r: F d* {9 }2 C) t
1)等待A0和B0的数据同步到其备服务器,即A1和B1。
; u' z3 c& E. h) i2)停止写服务,等待主备完全同步后解除A0与A1、B0与B1之间的主备关系。9 S9 K4 v. j1 p. R. K
3)修改中间层的映射规则,将哈希值模4等于1的用户数据映射到A1,哈希值模
6 P" z0 @5 c( n4 F7 Q4等于3的用户数据映射到B1。9 S5 k4 }! j8 t; L" H2 f$ G
4)开启写服务,用户id哈希值模4等于0、1、2、3的数据将分别写入到A0、
0 B" g- U+ D& I% `A1、B0、B1。这就相当于有一半的数据分别从A0、B0迁移到A1、B1。7 }& K& t) c: T3 d: t5 c+ Q
5)分别给A0、A1、B0、B1增加一台备机。
; w- C$ e/ g. v) Z最终,集群由2个dbgroup变为4个dbgroup。可以看到,扩容过程需要停一小会儿
( V- v2 Q& C) R8 {+ i服务,另外,扩容进行过程中如果再次发生服务器故障,将使扩容变得非常复杂,
$ t% t, {- d6 q1 r很难做到完全自动化。
: L1 ~7 ?" s+ [9 C9 L6 o对于第2个问题,可以在应用层定期统计大用户,并且将这些用户的数据按照数$ _5 w7 j: K$ y& {" f6 e
据量拆分到多个dbgroup。当然,定期维护这些信息对应用层是一个很大的代价。
y6 T! v8 P& A1 p) f" h7 t7.1.3 讨论
" W0 J0 O9 |8 l9 C6 E: A% d$ f# Y引入数据库中间层将后端分库分表对应用透明化在大型互联网公司内部很常8 X. ]% X2 u1 S$ L3 Q, a* v
见。这种做法实现简单,对应用友好,但也面临一些问题: H5 i3 r: p3 W' L% |" ]
●数据库复制:MySQL主备之间只支持异步复制,而且主库压力较大时可能产生' m) R( ]0 q: h" ^
很大的延迟,因此,主备切换可能会丢失最后一部分更新事务,这时往往需要人工* c+ {, K' i% r
介入。
6 j% b7 }' D" K, w R, N% a& ~+ c●扩容问题:如果系统压力过大需要增加新的机器,这个过程涉及数据重新划
; J3 }7 P, S1 d* F; W' @6 d8 ~) b T t分,整个过程比较复杂,且容易出错。& ] ?" ?8 S7 Z5 f& X
●动态数据迁移问题:如果某个数据库组压力过大,需要将其中部分数据迁移出* o5 G2 w q/ S+ f4 ~4 x+ h( Y4 [
去,迁移过程需要总控节点整体协调,以及数据库节点的配合。这个过程很难做到' d; | {! o* y; z. @
自动化。3 q' {. j+ F _( a0 {5 O' D
& j% c/ D! C1 }' e2 z2 i( r* @# G8 w; A( u9 s d
|
|