|
8.3 系统架构& m1 ]( i- C+ f! V
8.3.1 整体架构图 {% o Z9 v1 S k# Z
OceanBase的整体架构如图8-1所示。2 s% p% z6 M; l- g8 [
图 8-1 OceanBase整体架构图8 C0 u% {1 ^2 h+ ^
OceanBase由如下几个部分组成:
. A v. O2 a$ {6 {$ A●客户端:用户使用OceanBase的方式和MySQL数据库完全相同,支持JDBC、 C9 L/ M4 L/ z. y% A. C
客户端访问,等等。基于MySQL数据库开发的应用程序、工具能够直接迁移到8 X& E5 |& d6 s. H% R/ v, I
OceanBase。
4 l- {1 C3 I9 G6 r●RootServer:管理集群中的所有服务器,子表(tablet)数据分布以及副本管
! F& W0 O ~! f8 Z( f: N- K4 C理。 RootServer一般为一主一备,主备之间数据强同步。
|6 A9 ?- s$ _$ \8 o●UpdateServer:存储OceanBase系统的增量更新数据。UpdateServer一般为一主
* u" H$ Z, R6 M6 n一备,主备之间可以配置不同的同步模式。部署时,UpdateServer进程和RootServer o6 ^4 j" Z+ m
进程往往共用物理服务器。+ H+ ]" l1 J; F3 |) R
●ChunkServer:存储OceanBase系统的基线数据。基线数据一般存储两份或者三& n3 D2 K) L+ c# D/ j- N; [
份,可配置。
$ g% f: a5 a& W9 a●MergeServer:接收并解析用户的SQL请求,经过词法分析、语法分析、查询优, i' n3 k j* U4 g1 w+ x+ ^1 `
化等一系列操作后转发给相应的ChunkServer或者UpdateServer。如果请求的数据分布
* I" j/ k" _8 _/ |3 T0 l0 J4 x7 \在多台ChunkServer上,MergeServer还需要对多台ChunkServer返回的结果进行合并。' k* e* w; t8 A# \) Z9 C
客户端和MergeServer之间采用原生的MySQL通信协议,MySQL客户端可以直接访问
4 D6 h% V! l; C. A6 } C) rMergeServer。
4 E* Q% N8 W9 \OceanBase支持部署多个机房,每个机房部署一个包含RootServer、9 H! S; S/ f. G. X! G
MergeServer、ChunkServer以及UpdateServer的完整OceanBase集群,每个集群由各自! v% O' r" O/ t
的RootServer负责数据划分、负载均衡、集群服务器管理等操作,集群之间数据同步9 Z$ P1 `% n5 D" n
通过主集群的主UpdateServer往备集群同步增量更新操作日志实现。客户端配置了多% y$ x3 j. u6 j" |1 L8 v4 U
个集群的RootServer地址列表,使用者可以设置每个集群的流量分配比例,客户端根
0 o( b7 Y% N$ W7 |; W据这个比例将读写操作发往不同的集群。图8-2是双机房部署示意图。
4 o1 c8 L+ K+ N$ e& B图 8-2 OceanBase双机房部署
' ], R& N" C6 n' h! M( i8.3.2 客户端
0 W. e" J1 n7 ~OceanBase客户端与MergeServer通信,目前主要支持如下几种客户端:
5 W1 X4 z, ~/ p. k' A6 l1 f+ d●MySQL客户端:MergeServer兼容MySQL协议,MySQL客户端及相关工具(如
6 W+ U5 e" ^. U7 o% f3 uJava数据库访问方式JDBC)只需要将服务器的地址设置为任意一台Merge-Server的地
1 `4 B2 f7 E) B+ ?( [/ F; ?址,就可以直接使用。- O0 l2 ~! ~" ~/ ~
●Java客户端:OceanBase内部部署了多台MergeServer,Java客户端提供对 MySQL
: J5 O# D( B6 A3 y2 ]( I) s8 b6 J标准JDBC Driver的封装,并提供流量分配、负载均衡、MergeServer异常处理等功& x9 J% E$ k+ H w, P% Y
能。简单来讲,Java客户端首先按照一定的策略定位到某台MergeServer,接着调用
3 P: J4 b. `- q/ b: vMySQL JDBC Driver往这台MergeServer发送读写请求。Java客户端实现符合JDBC标
$ J# D: A/ J. P准,能够支持Spring、iBatis等Java编程框架。
) ~9 R6 Y7 ~8 a2 c" U! P; y●C客户端:OceanBase C客户端的功能和Java客户端类似。它首先按照一定的策2 j9 O g1 E: z- N# {9 O
略定位到某台MergeServer,接着调用MySQL标准C客户端往这台MergeServer发送读0 w4 A: Q3 F. C7 }+ t( B/ O
写请求。C客户端的接口和MySQL标准C客户端接口完全相同,因此,能够通过9 D7 i/ y3 P a! U" z' M
LD_PRELOAD的方式将应用程序依赖的MySQL标准C客户端替换为OceanBase C客户
8 u5 h4 b* {- m) V$ s9 C3 R端,而无需修改应用程序的代码。
( Z/ ]/ |" [! vOceanBase集群有多台MergeServer,这些MergeServer的服务器地址存储在
# X& m( R& @' u: {: V% s+ s9 B+ A7 uOceanBase服务器端的系统表(与Oracle的系统表类似,存储OceanBase系统的元数
0 J' x8 W' h# T+ c: r2 X/ X [据)内。OceanBase Java/C客户端首先请求服务器端获取MergeServer地址列表,接着( y* W! M6 o! p* j2 [( w# d
按照一定的策略将读写请求发送给某台MergeServer,并负责对出现故障的2 L- o; D' e9 c5 D Z6 P
MergeServer进行容错处理。+ j( K& F% v f5 `( m, H
Java/C客户端访问OceanBase的流程大致如下:
: K8 A2 k \' N1)请求RootServer获取集群中MergeServer的地址列表。* D& P: {5 T0 t5 B* s3 ~! `! ]6 H2 r
2)按照一定的策略选择某台MergeServer发送读写请求。客户端与MergeServer$ A0 R1 B" E d7 [
之间的通信协议兼容原生的MySQL协议,因此,只需要调用MySQL JDBC Driver或者
, I8 B) Q. z! c r0 tMySQL C客户端这样的标准库即可。客户端支持的策略主要有两种:随机以及一致* v- c4 p! v, K
性哈希。一致性哈希的主要目的是将相同的SQL请求发送到同一台MergeServer,方
6 p/ M0 X8 s# D+ Z9 T0 p便MergeServer对查询结果进行缓存。6 y. B& X) U9 d; l
3)如果请求MergeServer失败,则从MergeServer列表中重新选择一台
$ @ V/ J, f/ E; D) {MergeServer重试;如果请求某台MergeServer失败超过一定的次数,将这台
' W2 v* Z" Y! X2 `MergeServer加入黑名单并从MergeServer列表中删除。另外,客户端会定期请求
6 o* }- s" i9 W! ORootServer更新MergeServer地址列表。
) F& b1 b6 e* l5 M如果OceanBase部署多个集群,客户端还需要处理多个集群的流量分配问题。使& l$ D4 a! I% w1 E
用者可以设置多个集群之间的流量分配比例,客户端获取到流量分配比例后,按照! [* {7 [0 t. G# J9 T, H
这个比例将请求发送到不同的集群。! _0 [* w- |0 D
OceanBase程序升级版本时,往往先将备集群的读取流量调整为0,这时所有的- O% E) c( d0 W" N% J7 G1 T
读写请求都只发往主集群,接着升级备集群的程序版本。备集群升级完成后将流量
6 y# p: B* N4 T% K0 C逐步切换到备集群观察一段时间,如果没有出现异常,则将所有的流量切到备集
; @) d- y( q4 `: s9 }1 y群,并将备集群切换为主集群提供写服务。原来的主集群变为新的备集群,升级新 U$ Z; y+ Z9 S/ l5 L; m7 [
的备集群的程序版本后重新分配主备集群的流量比例。7 j$ G6 [$ D0 i& p4 T6 a- R
8.3.3 RootServer) Q6 U+ \8 {+ c* ^: r
RootServer的功能主要包括:集群管理、数据分布以及副本管理。
) m& w9 F1 L" |0 t; K( s: gRootServer管理集群中的所有MergeServer、ChunkServer以及UpdateServer。每个
' T$ O B' r9 S& T集群内部同一时刻只允许一个UpdateServer提供写服务,这个UpdateServer成为主) H" \5 ]4 m# ~4 Z1 n. Q
UpdateServer。这种方式通过牺牲一定的可用性获取了强一致性。RootServer通过租7 a) S/ `) }( B
约(Lease)机制选择唯一的主UpdateServer,当原先的主UpdateServer发生故障后,
9 W) P, `3 D; @- A Q- aRootServer能够在原先的租约失效后选择一台新的UpdateServer作为主UpdateServer。
8 P D5 }; L3 | G. ^$ |; M4 [; T另外,RootServer与MergeServer&ChunkServer之间保持心跳(heartbeat),从而能够0 \# R( `- D7 r( F
感知到在线和已经下线的MergeServer&ChunkServer机器列表。0 P0 o2 E/ c) @5 |+ A" A5 D
OceanBase内部使用主键对表格中的数据进行排序和存储,主键由若干列组成并0 R, O1 H; j# S+ Y! s# h* p
且具有唯一性。在OceanBase内部,基线数据按照主键排序并且划分为数据量大致相
. u1 h" ^' s, u$ m7 }* E6 E等的数据范围,称为子表(tablet)。每个子表的默认大小是256MB(可配置)。: j5 R H* @+ x, b f
OceanBase的数据分布方式与Bigtable一样采用顺序分布,不同的是,OceanBase没有
$ M- u5 ^6 W+ @9 Q' e采用根表(RootTable)+元数据表(MetaTable)两级索引结构,而是采用根表一级
& q4 O& e9 i" p$ S( {% M索引结构。, e: O3 \3 V# Q& O9 x7 Y5 a9 ?3 Q
如图8-3所示,主键值在[1,100]之间的表格被划分为四个子表:1~25,26~. k4 {$ m' T+ K! c1 z2 k1 _* G# D' U% V
50,51~80以及81~100。RootServer中的根表记录了每个子表所在的ChunkServer位: ^. f+ o% S& H5 u! q" ]
置信息,每个子表包含多个副本(一般为三个副本,可配置),分布在多台& k- d+ V% c8 ]: T% e" R
ChunkServer中。当其中某台ChunkServer发生故障时,RootServer能够检测到,并且触
# Z9 a& d( x' p, _% P2 A发对这台ChunkServer上的子表增加副本的操作;另外,RootServer也会定期执行负载
6 s. Q1 n5 J0 g) M/ B& t. d均衡,选择某些子表从负载较高的机器迁移到负载较低的机器上。
+ b/ v% \1 J5 ~1 @图 8-3 基线数据子表划分
1 Y0 E) z8 O/ k% m3 zRootServer采用一主一备的结构,主备之间数据强同步,并通过Linux& y B8 A# H$ K/ d6 `$ V1 s" ?
HA(http://www.linux-ha.org)软件实现高可用性。主备RootServer之间共享VIP,当( \ v8 _( P ?2 ~
主RootServer发生故障后,VIP能够自动漂移到备RootServer所在的机器,备: _- y) r' x: N
RootServer检测到以后切换为主RootServer提供服务。 K$ K; u. T+ |1 x8 B3 r9 b" ~
8.3.4 MergeServer. Q( x% ~ [9 O+ K. u
MergeServer的功能主要包括:协议解析、SQL解析、请求转发、结果合并、多( N3 K4 N" f$ \* ~
表操作等。; G' t6 X1 k2 p3 P7 r* B: }
OceanBase客户端与MergeServer之间的协议为MySQL协议。MergeServer首先解
) y O& Z$ Z* x; T1 Q: d$ Q析MySQL协议,从中提取出用户发送的SQL语句,接着进行词法分析和语法分析,
3 s# Z: i! d/ Y& N. k w生成SQL语句的逻辑查询计划和物理查询计划,最后根据物理查询计划调用
+ |2 k: q3 g; F. c( K# z: S$ O5 |OceanBase内部的各种操作符。* i& v5 I. f; n7 r: X
MergeServer缓存了子表分布信息,根据请求涉及的子表将请求转发给该子表所
& @1 [& M! u/ T" T+ m' m- a, u在的ChunkServer。如果是写操作,还会转发给UpdateServer。某些请求需要跨多个子1 s7 j- E4 y8 [; K
表,此时MergeServer会将请求拆分后发送给多台ChunkServer,并合并这些2 ~* }% z9 }4 b% x
ChunkServer返回的结果。如果请求涉及多个表格,MergeServer需要首先从: d3 r5 Z3 [. x+ n. H0 g
ChunkServer获取每个表格的数据,接着再执行多表关联或者嵌套查询等操作。
" \$ g5 t4 d& C' K4 f% N( TMergeServer支持并发请求多台ChunkServer,即将多个请求发给多台
0 p% w5 J, J3 MChunkServer,再一次性等待所有请求的应答。另外,在SQL执行过程中,如果某个- j8 y5 g. @) A8 T, H
子表所在的ChunkServer出现故障,MergeServer会将请求转发给该子表的其他副本所1 @) ~' n7 d- V
在的ChunkServer。这样,ChunkServer故障是不会影响用户查询的。
# b; r/ [9 y6 A/ } S" S3 bMergeServer本身是没有状态的,因此,MergeServer宕机不会对使用者产生影+ Y) q) j8 `- ~
响,客户端会自动将发生故障的MergeServer屏蔽掉。" M% }. v- z) c1 |) {/ Y
8.3.5 ChunkServer/ {& w, f4 E6 A, p% W# D
ChunkServer的功能包括:存储多个子表,提供读取服务,执行定期合并以及数3 _* p! m' W# X$ M; H
据分发。
; G4 E. K/ V; F5 h0 e4 l- ]OceanBase将大表划分为大小约为256MB的子表,每个子表由一个或者多个: S( t" ]! i- D% G
SSTable组成(一般为一个),每个SSTable由多个块(Block,大小为4KB~64KB之
9 z# n+ ^( H, d, M4 c8 E2 h间,可配置)组成,数据在SSTable中按照主键有序存储。查找某一行数据时,需要
- I8 B0 c* U5 S' Q首先定位这一行所属的子表,接着在相应的SSTable中执行二分查找。SSTable支持两( ]& {/ Q; @* @ V, Y
种缓存模式,块缓存(Block Cache)以及行缓存(Row Cache)。块缓存以块为单位
7 H) n) X+ A5 Z2 ?缓存最近读取的数据,行缓存以行为单位缓存最近读取的数据。9 l6 S2 q1 q9 {) Q) R m; c" q! M
MergeServer将每个子表的读取请求发送到子表所在的ChunkServer,ChunkServer首
6 Q* e# y. q+ i) B先读取SSTable中包含的基线数据,接着请求UpdateServer获取相应的增量更新数据,
1 R% f- l9 @) V$ H3 x并将基线数据与增量更新融合后得到最终结果。' h( L g9 b2 D( s9 _
由于每次读取都需要从UpdateServer中获取最新的增量更新,为了保证读取性! Z$ c2 x6 l; W: \1 i
能,需要限制UpdateServer中增量更新的数据量,最好能够全部存放在内存中。3 b$ L+ z+ ]8 c8 D/ F9 d. F8 E
OceanBase内部会定期触发合并或者数据分发操作,在这个过程中,ChunkServer将从
1 X1 X3 Y2 w1 C: jUpdateServer获取一段时间之前的更新操作。通常情况下,OceanBase集群会在每天6 @' ^7 X* S" ^5 v8 P9 o9 `( g$ n0 f2 k
的服务低峰期(凌晨1:00开始,可配置)执行一次合并操作。这个合并操作往往也称
! @4 a6 I7 V; h v为每日合并。
9 C) ^4 v3 H& x& m% \8.3.6 UpdateServer' {8 y% Y+ F" O5 N! O, e4 Y; f
UpdateServer是集群中唯一能够接受写入的模块,每个集群中只有一个主Update-9 B' T0 X- m* Q/ |# G' V
Server。UpdateServer中的更新操作首先写入到内存表,当内存表的数据量超过一定
& k( W V1 D% U值时,可以生成快照文件并转储到SSD中。快照文件的组织方式与ChunkServer中的/ l6 L6 u7 t& l, F# _7 f2 p
SSTable类似,因此,这些快照文件也称为SSTable。另外,由于数据行的某些列被更
9 w |* |, }+ K( G, p$ p/ o新,某些列没被更新,SSTable中存储的数据行是稀疏的,称为稀疏型SSTable。: ]5 u& ^& M( }4 Q. S0 x3 R4 G
为了保证可靠性,主UpdateServer更新内存表之前需要首先写操作日志,并同步
1 ~; \% `" D3 F! h) m; u( h到备UpdateServer。当主UpdateServer发生故障时,RootServer上维护的租约将失效,
2 \# z$ J8 R" f$ ?4 S$ h此时,RootServer将从备UpdateServer列表中选择一台最新的备UpdateServer切换为主6 S j( s% B4 n4 \7 t7 S: u
UpdateServer继续提供写服务。UpdateServer宕机重启后需要首先加载转储的快照文" {! L7 z5 [! ^( a4 J1 Y; E5 [
件(SSTable文件),接着回放快照点之后的操作日志。% B; ?; b1 {4 Q; @( Y @/ U
由于集群中只有一台主UpdateServer提供写服务,因此,OceanBase很容易地实$ p1 m( @. ]: E. @, H E' d
现了跨行跨表事务,而不需要采用传统的两阶段提交协议。当然,这样也带来了一5 g3 {# D7 F+ j3 N% |+ J
系列的问题。由于整个集群所有的读写操作都必须经过UpdateServer,UpdateServer的" E0 n4 m$ p# s% j2 n
性能至关重要。OceanBase集群通过定期合并和数据分发这两种机制将UpdateServer
* ?0 x* S# D, D( ]2 N一段时间之前的增量更新源源不断地分散到ChunkServer,而UpdateServer只需要服务
+ D3 t- i/ k; q- t. D最新一小段时间新增的数据,这些数据往往可以全部存放在内存中。另外,系统实
1 D4 m9 R; P$ _2 ?现时也需要对UpdateServer的内存操作、网络框架、磁盘操作做大量的优化。
# `, T" A6 R/ U2 U K0 i3 G8.3.7 定期合并&数据分发2 c. N8 }6 B" ]- M; o/ ]1 z
定期合并和数据分发都是将UpdateServer中的增量更新分发到ChunkServer中的手
$ I, R& r% L0 c段,二者的整体流程比较类似:1 v g; j9 G4 N5 _
1)UpdateServer冻结当前的活跃内存表(Active MemTable),生成冻结内存
: G) b7 G. A7 ]% \表,并开启新的活跃内存表,后续的更新操作都写入新的活跃内存表。1 {0 J# X5 u; q
2)UpdateServer通知RootServer数据版本发生了变化,之后RootServer通过心跳: V" A& m% E3 s; x4 P+ z
消息通知ChunkServer。
. ]; [( Q! P1 n, D1 d3)每台ChunkServer启动定期合并或者数据分发操作,从UpdateServer获取每个# [0 l0 w: D+ f- R
子表对应的增量更新数据。
J2 y$ p5 ?! D. h定期合并与数据分发两者之间的不同点在于,数据分发过程中ChunkServer只是
6 x$ w" R& C& q将UpdateServer中冻结内存表中的增量更新数据缓存到本地,而定期合并过程中+ D/ e1 K& L5 ?$ N0 b" o
ChunkServer需要将本地SSTable中的基线数据与冻结内存表的增量更新数据执行一次
" S2 O" H; Y% k& t @ y, u2 D多路归并,融合后生成新的基线数据并存放到新的SSTable中。定期合并对系统服务' r) k; x6 `$ f7 j, r+ [% p! j
能力影响很大,往往安排在每天服务低峰期执行(例如凌晨1点开始),而数据分发
]* B0 t8 R; f, k* e9 {可以不受限制。2 {2 C4 w1 s3 H! V4 S; f1 X: V/ o
如图8-4,活跃内存表冻结后生成冻结内存表,后续的写操作进入新的活跃内存' H, s# k5 Z7 b. Y
表。定期合并过程中ChunkServer需要读取UpdateServer中冻结内存表的数据、融合后
2 L: f' w4 F2 {) v生成新的子表,即:3 x! M4 \2 Z; ?# [1 T7 B
新子表=旧子表+冻结内存表
; ~! ^$ d; }0 ~0 |- h3 V图 8-4 定期合并不停读服务1 [% r# M. [% }( x" {
虽然定期合并过程中各个ChunkServer的各个子表合并时间和完成时间可能都不" t7 p* q: Q& U
相同,但并不影响读取服务。如果子表没有合并完成,那么使用旧子表,并且读取
# L( J2 d9 s" n8 FUpdateServer中的冻结内存表以及新的活跃内存表;否则,使用新子表,只读取新的4 T0 h. O6 `8 \7 X/ ~
活跃内存表,即:
% Y' C1 @; y. n" h E4 s$ r查询结果=旧子表+冻结内存表+新的活跃内存表
7 h4 s5 K/ M. \1 r=新子表+新的活跃内存表
0 m+ Y% k2 b( e, R" N; O, }
" r( R3 n. j9 M$ A& U4 M. y
0 L) K; j* p! `7 k6 b |
|