|
8.3 系统架构
/ [0 x$ l* [ V7 K8.3.1 整体架构图
' D3 ~+ t! G5 r( r$ f+ s6 SOceanBase的整体架构如图8-1所示。- ^' a0 H. ]- R+ A. c
图 8-1 OceanBase整体架构图
; i G! r! } k/ NOceanBase由如下几个部分组成:
! h" U% e8 B0 K0 a●客户端:用户使用OceanBase的方式和MySQL数据库完全相同,支持JDBC、 C
; h1 N6 l4 g5 f, ]客户端访问,等等。基于MySQL数据库开发的应用程序、工具能够直接迁移到
2 ?* c+ z1 g/ H! I" X6 N0 JOceanBase。
u2 \& n; ^2 Q" l& p! M1 _: J●RootServer:管理集群中的所有服务器,子表(tablet)数据分布以及副本管
- @8 M$ ?4 ]4 F' L+ a* f( a0 |( |理。 RootServer一般为一主一备,主备之间数据强同步。' L3 P4 A. k- j! ~& B
●UpdateServer:存储OceanBase系统的增量更新数据。UpdateServer一般为一主
* M+ B3 A1 A+ _- Y Q D$ J) |一备,主备之间可以配置不同的同步模式。部署时,UpdateServer进程和RootServer1 S( y& F6 ~. z" h7 {
进程往往共用物理服务器。
D0 P/ z. |' C$ Q+ k●ChunkServer:存储OceanBase系统的基线数据。基线数据一般存储两份或者三' q* u( o h: i5 r6 N8 l
份,可配置。
1 }+ r1 a: O" r2 ?●MergeServer:接收并解析用户的SQL请求,经过词法分析、语法分析、查询优
, u5 {. d4 H2 h+ ]* P1 v- `0 z化等一系列操作后转发给相应的ChunkServer或者UpdateServer。如果请求的数据分布: k* Z. F+ a+ s. E, N. m% U8 h+ F
在多台ChunkServer上,MergeServer还需要对多台ChunkServer返回的结果进行合并。. V5 v H5 o- Z- ^: b: C+ X1 {
客户端和MergeServer之间采用原生的MySQL通信协议,MySQL客户端可以直接访问3 v% S: m4 f& I9 {+ E6 |" I
MergeServer。/ g; Y. {$ i! B/ N, t
OceanBase支持部署多个机房,每个机房部署一个包含RootServer、
3 Z( \: D4 M, q" y8 k4 IMergeServer、ChunkServer以及UpdateServer的完整OceanBase集群,每个集群由各自! P0 [. V+ M& N0 Q5 [
的RootServer负责数据划分、负载均衡、集群服务器管理等操作,集群之间数据同步' Z9 u& E2 s4 R0 z! q' S0 j0 ~, E. S
通过主集群的主UpdateServer往备集群同步增量更新操作日志实现。客户端配置了多3 G+ v1 m+ { D7 C; F: E
个集群的RootServer地址列表,使用者可以设置每个集群的流量分配比例,客户端根5 q1 |# N" x( s2 Z
据这个比例将读写操作发往不同的集群。图8-2是双机房部署示意图。" Q! ] U+ }4 V. R. _: y. h
图 8-2 OceanBase双机房部署
" ?( q% ]! |) }3 l8.3.2 客户端
0 f9 G% A& Y+ q6 b n( h6 lOceanBase客户端与MergeServer通信,目前主要支持如下几种客户端:
0 t8 i% \7 N; k, z+ S" R, y●MySQL客户端:MergeServer兼容MySQL协议,MySQL客户端及相关工具(如 Q3 p) i6 H( S. c! y3 P/ t2 U2 @
Java数据库访问方式JDBC)只需要将服务器的地址设置为任意一台Merge-Server的地
( t3 U- i- c- k0 V& V址,就可以直接使用。
7 ~% X# h7 b( N) g' w●Java客户端:OceanBase内部部署了多台MergeServer,Java客户端提供对 MySQL
9 ` k( F) h y2 g) e标准JDBC Driver的封装,并提供流量分配、负载均衡、MergeServer异常处理等功
4 ]0 T6 [; \$ ?* Y$ i5 g! x4 W; O能。简单来讲,Java客户端首先按照一定的策略定位到某台MergeServer,接着调用) G2 g* h; b7 F; i) h
MySQL JDBC Driver往这台MergeServer发送读写请求。Java客户端实现符合JDBC标
& U; w& c7 k; l, Z% ?* f3 B I0 @. o准,能够支持Spring、iBatis等Java编程框架。
# u8 D. X6 P$ H$ A4 g1 f: m4 a; S0 R) T●C客户端:OceanBase C客户端的功能和Java客户端类似。它首先按照一定的策
7 D7 q; H# b+ K; z" A略定位到某台MergeServer,接着调用MySQL标准C客户端往这台MergeServer发送读+ y# r9 F, z1 e& M; [3 y
写请求。C客户端的接口和MySQL标准C客户端接口完全相同,因此,能够通过5 b0 I8 {' F" q, Q, f/ F
LD_PRELOAD的方式将应用程序依赖的MySQL标准C客户端替换为OceanBase C客户" x' h3 Y# {+ Q. E5 x
端,而无需修改应用程序的代码。) l4 n- [% p9 h, \' U3 h' T
OceanBase集群有多台MergeServer,这些MergeServer的服务器地址存储在
4 ]6 r0 q& q9 w! S+ GOceanBase服务器端的系统表(与Oracle的系统表类似,存储OceanBase系统的元数8 t3 n% M$ f3 C# T0 C& b; }
据)内。OceanBase Java/C客户端首先请求服务器端获取MergeServer地址列表,接着/ d) S1 r2 F' ?9 Z# X
按照一定的策略将读写请求发送给某台MergeServer,并负责对出现故障的
l/ Z6 y) V1 L9 p4 c. G+ z0 ]9 yMergeServer进行容错处理。+ a. w" w8 E5 |% h) D
Java/C客户端访问OceanBase的流程大致如下:4 U7 B: E1 l: u1 ]
1)请求RootServer获取集群中MergeServer的地址列表。
6 i: r- v0 T+ s$ J& Y' G2)按照一定的策略选择某台MergeServer发送读写请求。客户端与MergeServer+ |3 W: s! l+ t3 X( `* Z' p
之间的通信协议兼容原生的MySQL协议,因此,只需要调用MySQL JDBC Driver或者
( O9 T7 m7 r C% A% MMySQL C客户端这样的标准库即可。客户端支持的策略主要有两种:随机以及一致
! c) _( \( u( H7 A8 }性哈希。一致性哈希的主要目的是将相同的SQL请求发送到同一台MergeServer,方: h5 X; P2 G/ e |4 z5 {6 S+ @, y( x
便MergeServer对查询结果进行缓存。7 \- Z; y3 G) Q& |2 X
3)如果请求MergeServer失败,则从MergeServer列表中重新选择一台
" V( t) b9 \" }9 {/ _3 e9 eMergeServer重试;如果请求某台MergeServer失败超过一定的次数,将这台% O& H) Y) X4 H
MergeServer加入黑名单并从MergeServer列表中删除。另外,客户端会定期请求
* u* |$ X/ g oRootServer更新MergeServer地址列表。* b3 `: G" g8 @. _
如果OceanBase部署多个集群,客户端还需要处理多个集群的流量分配问题。使
+ [4 L$ x- b8 L0 h( Z2 x用者可以设置多个集群之间的流量分配比例,客户端获取到流量分配比例后,按照! V& g, K" M% X1 R
这个比例将请求发送到不同的集群。
. ^5 N2 l5 y, J5 mOceanBase程序升级版本时,往往先将备集群的读取流量调整为0,这时所有的
2 ^ p3 a+ y1 d$ v6 F0 G3 e" N5 V0 v读写请求都只发往主集群,接着升级备集群的程序版本。备集群升级完成后将流量: E8 p8 }. Q9 K: U$ w
逐步切换到备集群观察一段时间,如果没有出现异常,则将所有的流量切到备集
6 M7 U( {9 P, p) E9 y8 f4 Q0 P3 y群,并将备集群切换为主集群提供写服务。原来的主集群变为新的备集群,升级新
0 t# \3 ^$ N. }$ v; w的备集群的程序版本后重新分配主备集群的流量比例。0 X v5 x$ o6 E) G4 u+ |$ k
8.3.3 RootServer
0 f+ k! w- G& I& U; ?RootServer的功能主要包括:集群管理、数据分布以及副本管理。
7 n# U! L4 I! L, Z% VRootServer管理集群中的所有MergeServer、ChunkServer以及UpdateServer。每个
. b7 B2 F, z( l; F, e, F0 S A集群内部同一时刻只允许一个UpdateServer提供写服务,这个UpdateServer成为主5 i1 ?8 i( |+ U$ H3 h0 G% s
UpdateServer。这种方式通过牺牲一定的可用性获取了强一致性。RootServer通过租
2 d' y4 t$ z) H约(Lease)机制选择唯一的主UpdateServer,当原先的主UpdateServer发生故障后,
' n- z; e4 a9 S: NRootServer能够在原先的租约失效后选择一台新的UpdateServer作为主UpdateServer。
. @2 o- ^7 q7 Z& X另外,RootServer与MergeServer&ChunkServer之间保持心跳(heartbeat),从而能够
9 J8 K- X; m( I; X) H感知到在线和已经下线的MergeServer&ChunkServer机器列表。
# n2 Q, o' u) d* T$ X* Q3 \& }OceanBase内部使用主键对表格中的数据进行排序和存储,主键由若干列组成并
/ r4 P9 B# _6 g/ Y7 D+ K, e且具有唯一性。在OceanBase内部,基线数据按照主键排序并且划分为数据量大致相0 O+ h; h- D' K! {/ Q& f
等的数据范围,称为子表(tablet)。每个子表的默认大小是256MB(可配置)。. ]# @+ Y9 T: ?# V5 {
OceanBase的数据分布方式与Bigtable一样采用顺序分布,不同的是,OceanBase没有
v" a6 i+ P& i0 ]采用根表(RootTable)+元数据表(MetaTable)两级索引结构,而是采用根表一级
* @: o3 h+ h; E索引结构。( y0 d9 }6 \. _( ?/ v6 Z
如图8-3所示,主键值在[1,100]之间的表格被划分为四个子表:1~25,26~0 g5 D4 ^' W+ W# t
50,51~80以及81~100。RootServer中的根表记录了每个子表所在的ChunkServer位
7 X. O$ r4 k0 B: `: W+ j4 {8 l置信息,每个子表包含多个副本(一般为三个副本,可配置),分布在多台1 F. T! t. I! {% k% \! ?& a5 A
ChunkServer中。当其中某台ChunkServer发生故障时,RootServer能够检测到,并且触
3 E/ d. @& Y$ U) G* b) _9 ]) n发对这台ChunkServer上的子表增加副本的操作;另外,RootServer也会定期执行负载9 g: r9 l2 d* q7 J" N
均衡,选择某些子表从负载较高的机器迁移到负载较低的机器上。7 R$ S8 v# h) F4 u. v
图 8-3 基线数据子表划分
& d4 ?+ W6 Q5 Q) o1 SRootServer采用一主一备的结构,主备之间数据强同步,并通过Linux
0 v3 g1 N) m2 g* ]! m* ~HA(http://www.linux-ha.org)软件实现高可用性。主备RootServer之间共享VIP,当9 @5 d* c' ]0 ]! e4 z' ?- D
主RootServer发生故障后,VIP能够自动漂移到备RootServer所在的机器,备
) D) p! \" U9 ~* S, vRootServer检测到以后切换为主RootServer提供服务。* B4 z1 b8 o4 n
8.3.4 MergeServer0 h# X, s7 }' |
MergeServer的功能主要包括:协议解析、SQL解析、请求转发、结果合并、多4 x3 M. s+ g1 m. J( ]
表操作等。
2 E4 j4 F6 W1 d: EOceanBase客户端与MergeServer之间的协议为MySQL协议。MergeServer首先解
2 x/ Z& p7 o. B; X2 W/ H析MySQL协议,从中提取出用户发送的SQL语句,接着进行词法分析和语法分析,8 L" a" g$ W+ Y+ ^6 e
生成SQL语句的逻辑查询计划和物理查询计划,最后根据物理查询计划调用
7 Q8 F8 o$ A3 k/ U! h7 w" M- kOceanBase内部的各种操作符。. \, e* S. }) L; F% k1 R7 H
MergeServer缓存了子表分布信息,根据请求涉及的子表将请求转发给该子表所8 }' b* s1 b5 ^/ J
在的ChunkServer。如果是写操作,还会转发给UpdateServer。某些请求需要跨多个子7 _ }" b* O4 x& g0 C7 D! w' e& X& m
表,此时MergeServer会将请求拆分后发送给多台ChunkServer,并合并这些9 g* |" p6 \% ^ e3 k; V1 e' Q+ B
ChunkServer返回的结果。如果请求涉及多个表格,MergeServer需要首先从
) I6 a8 d0 `. W4 CChunkServer获取每个表格的数据,接着再执行多表关联或者嵌套查询等操作。1 u+ D! t# c. a
MergeServer支持并发请求多台ChunkServer,即将多个请求发给多台& h7 D# M. ]( o; {' s2 z9 ]' i6 y
ChunkServer,再一次性等待所有请求的应答。另外,在SQL执行过程中,如果某个) _. u; ^- X" @8 E# L' T
子表所在的ChunkServer出现故障,MergeServer会将请求转发给该子表的其他副本所
* [' b7 }' d$ c' m7 z2 q9 B& ]在的ChunkServer。这样,ChunkServer故障是不会影响用户查询的。6 n- C3 M& F% C6 `# E' [5 o, w
MergeServer本身是没有状态的,因此,MergeServer宕机不会对使用者产生影
5 p9 y! Z7 S$ ]. w7 Y响,客户端会自动将发生故障的MergeServer屏蔽掉。
+ k0 D' A. R( O H# j4 a) @- f8.3.5 ChunkServer# {/ g2 g6 x, r& M
ChunkServer的功能包括:存储多个子表,提供读取服务,执行定期合并以及数* |: }6 J" H' m
据分发。
' H. q) H7 N4 m( A: @3 XOceanBase将大表划分为大小约为256MB的子表,每个子表由一个或者多个# S5 _2 V0 X$ p6 b4 ]" @5 S( Q
SSTable组成(一般为一个),每个SSTable由多个块(Block,大小为4KB~64KB之7 X' b% A, Y4 a" _ Q* ~
间,可配置)组成,数据在SSTable中按照主键有序存储。查找某一行数据时,需要
& |1 O# t; K% I* s [( s% o首先定位这一行所属的子表,接着在相应的SSTable中执行二分查找。SSTable支持两 P c& ~2 W- x4 N4 U2 B0 c, l
种缓存模式,块缓存(Block Cache)以及行缓存(Row Cache)。块缓存以块为单位0 o: D, k8 U0 J7 R2 H2 n0 a
缓存最近读取的数据,行缓存以行为单位缓存最近读取的数据。
' g/ r. ~* q4 |) IMergeServer将每个子表的读取请求发送到子表所在的ChunkServer,ChunkServer首5 V5 }/ q) o" ~6 S# N! D
先读取SSTable中包含的基线数据,接着请求UpdateServer获取相应的增量更新数据,
$ ?& F0 O& J* A/ B _( N$ o并将基线数据与增量更新融合后得到最终结果。% ?6 ]+ k% K% i0 H0 G4 h% @
由于每次读取都需要从UpdateServer中获取最新的增量更新,为了保证读取性' j' `5 i; u: L. D4 D
能,需要限制UpdateServer中增量更新的数据量,最好能够全部存放在内存中。
5 Y$ Y" }! `4 M) J) [+ EOceanBase内部会定期触发合并或者数据分发操作,在这个过程中,ChunkServer将从, J# j: g4 U P8 }+ i
UpdateServer获取一段时间之前的更新操作。通常情况下,OceanBase集群会在每天
. e. ^/ i+ f @* w的服务低峰期(凌晨1:00开始,可配置)执行一次合并操作。这个合并操作往往也称
% P# ]: T I) C1 N1 J为每日合并。
. ?8 o0 F2 |5 k' U# B! o+ C* s, h8.3.6 UpdateServer
/ T3 S! A" `- c3 H* g$ y; eUpdateServer是集群中唯一能够接受写入的模块,每个集群中只有一个主Update-# ]! T4 N0 Z, F
Server。UpdateServer中的更新操作首先写入到内存表,当内存表的数据量超过一定
4 {+ j" _+ u4 i0 s9 A2 ?/ v g# x值时,可以生成快照文件并转储到SSD中。快照文件的组织方式与ChunkServer中的
. h6 c7 z6 y- r& F3 J/ eSSTable类似,因此,这些快照文件也称为SSTable。另外,由于数据行的某些列被更2 }8 \" h9 z) z2 e
新,某些列没被更新,SSTable中存储的数据行是稀疏的,称为稀疏型SSTable。. l. ~* D; ?- x, ]4 z. r& ^! R
为了保证可靠性,主UpdateServer更新内存表之前需要首先写操作日志,并同步. m+ r, K& x/ I9 k1 u- Q/ M2 o( t
到备UpdateServer。当主UpdateServer发生故障时,RootServer上维护的租约将失效,' v7 l6 A* _! ]/ o# F! b2 o
此时,RootServer将从备UpdateServer列表中选择一台最新的备UpdateServer切换为主# h g; N& h/ K
UpdateServer继续提供写服务。UpdateServer宕机重启后需要首先加载转储的快照文
2 _7 x/ m' V7 s, O" c/ I件(SSTable文件),接着回放快照点之后的操作日志。$ R5 g7 m. n3 @; \' ]" l
由于集群中只有一台主UpdateServer提供写服务,因此,OceanBase很容易地实
& X% q3 n" f+ z4 H3 S+ m9 Z现了跨行跨表事务,而不需要采用传统的两阶段提交协议。当然,这样也带来了一* }: ^# X/ i* z2 G* g2 N* N( u* {
系列的问题。由于整个集群所有的读写操作都必须经过UpdateServer,UpdateServer的" ~* o# s& e# ?: S" f, t
性能至关重要。OceanBase集群通过定期合并和数据分发这两种机制将UpdateServer
( K2 g8 ^: ^) o, B一段时间之前的增量更新源源不断地分散到ChunkServer,而UpdateServer只需要服务3 C" n( ]. W0 u" T: p
最新一小段时间新增的数据,这些数据往往可以全部存放在内存中。另外,系统实
: B+ {* }, I% ?8 n. ^" u现时也需要对UpdateServer的内存操作、网络框架、磁盘操作做大量的优化。) V2 P0 _" L; W# i
8.3.7 定期合并&数据分发
& F* Q: P6 i" @6 e定期合并和数据分发都是将UpdateServer中的增量更新分发到ChunkServer中的手
9 }- e4 @( G' k8 {! O段,二者的整体流程比较类似:7 U7 v' |! z2 M3 I
1)UpdateServer冻结当前的活跃内存表(Active MemTable),生成冻结内存' @& x# g1 a- C! a3 O! q6 O- S
表,并开启新的活跃内存表,后续的更新操作都写入新的活跃内存表。9 Q/ K. x" g9 e' Z
2)UpdateServer通知RootServer数据版本发生了变化,之后RootServer通过心跳
2 [) a/ ~2 k5 N( [- t3 P; d消息通知ChunkServer。& Z+ P! Q. v( r& @
3)每台ChunkServer启动定期合并或者数据分发操作,从UpdateServer获取每个8 L" R0 J( P8 u4 |
子表对应的增量更新数据。
: Q5 o) C/ j) f- ?定期合并与数据分发两者之间的不同点在于,数据分发过程中ChunkServer只是
2 q8 O. O% \1 p) n4 T, Q将UpdateServer中冻结内存表中的增量更新数据缓存到本地,而定期合并过程中
, d& C$ \. S* i# s# ^; lChunkServer需要将本地SSTable中的基线数据与冻结内存表的增量更新数据执行一次
& `6 i; w$ r% j: X- R多路归并,融合后生成新的基线数据并存放到新的SSTable中。定期合并对系统服务
; L5 W0 i& \7 s r( A7 O1 @& M1 p能力影响很大,往往安排在每天服务低峰期执行(例如凌晨1点开始),而数据分发+ b- i8 `* Z- N' @/ O3 m2 R
可以不受限制。
4 [3 N7 d* l* p0 O9 ]0 A. {如图8-4,活跃内存表冻结后生成冻结内存表,后续的写操作进入新的活跃内存
' M( h2 J7 k3 N) B3 H8 ?表。定期合并过程中ChunkServer需要读取UpdateServer中冻结内存表的数据、融合后
+ |: K/ @6 l9 k6 @" m生成新的子表,即:& P* T* h' J0 q# ~3 O8 P6 J
新子表=旧子表+冻结内存表
" M: t" D e- q1 w, K图 8-4 定期合并不停读服务! o* s! M1 s* u7 Y q$ z5 j
虽然定期合并过程中各个ChunkServer的各个子表合并时间和完成时间可能都不
4 j5 R6 _8 B4 D0 m相同,但并不影响读取服务。如果子表没有合并完成,那么使用旧子表,并且读取; ~0 m& k! F! u$ q
UpdateServer中的冻结内存表以及新的活跃内存表;否则,使用新子表,只读取新的" k, E& o4 U( C$ l7 e
活跃内存表,即:/ U' P' n/ S" J: S: ^
查询结果=旧子表+冻结内存表+新的活跃内存表
' v5 L0 ]& R1 o1 T=新子表+新的活跃内存表
2 Z1 p! G- \( M$ S% r! I3 _ }/ E& L, P
3 b# H9 m: a3 p, l/ F
|
|