OceanBase是一個通用的分布式的關(guān)系型數(shù)據(jù)庫,有很多獨(dú)特的特點(diǎn)。比如數(shù)據(jù)庫的多租戶、高可用、極致彈性伸縮能力。如果把OceanBase當(dāng)作單庫使用,就沒有把OceanBase的分布式優(yōu)勢發(fā)揮到極致。 本文主要分享一個基于分布式架構(gòu)的應(yīng)用把OceanBase數(shù)據(jù)庫的分布式優(yōu)勢發(fā)揮到極致所需要了解的OceanBase基礎(chǔ),這也是理解螞蟻金服的基于OceanBase構(gòu)建的三地五中心異地多活架構(gòu)的基礎(chǔ)。 分布式數(shù)據(jù)庫開發(fā)相關(guān)問題好的性能首先是設(shè)計出來的,應(yīng)用如果追求極致的性能,就需要關(guān)注OceanBase里數(shù)據(jù)的相關(guān)事情。如:
這些問題對理解OceanBase的分布式特點(diǎn)很有幫助。后面我們逐步看看OceanBase是如何應(yīng)對。 OceanBase集群外觀首先簡介一下OceanBase集群的外觀。 圖 1 OceanBase集群外觀 OceanBase是以集群形式運(yùn)行的,由一堆服務(wù)器組成。上圖是「三副本」部署,機(jī)器會分為三組,每組一個區(qū)域(稱為Zone),各個機(jī)器通過網(wǎng)絡(luò)互相訪問。沒有光纖交換機(jī)、共享存儲以及直連網(wǎng)線等。 服務(wù)器通常建議CPU、內(nèi)存和磁盤盡可能的大,磁盤建議用普通SSD盤。普通服務(wù)器的好處是便宜,劣勢是可靠性和性能可能不如小型機(jī)那么高。也就是說OceanBase可以部署在一組可靠性和性能不是特別高的普通服務(wù)器上,卻提供了高性能、高可用和高可靠、彈性伸縮等多項能力。 以上是一個OceanBase集群的外觀和能力,但是提供給業(yè)務(wù)的并不是這個集群的全部資源和能力,而是其子集,即租戶(Tenant)。 OceanBase多租戶特性OceanBase定義了一些基本的資源規(guī)格(Resource unit config,如4CPU8Gmem500Gdisk等),然后選取某類資源規(guī)格創(chuàng)建一組資源池(Resource Pool),此時集群資源就有一部分被分配出去了。最后將這個資源池關(guān)聯(lián)到一個新建租戶,則租戶就可以使用這個資源池的能力。 OceanBase默認(rèn)有個sys租戶,管理整個集群。用戶租戶必須在sys租戶內(nèi)部創(chuàng)建。 如下示例就是創(chuàng)建租戶的過程。 #sys租戶登錄方法 $mysql -hxxx.xx.11.11 -uroot@sys#obdemo -P2883 -proot oceanbase -A #資源規(guī)格(UnitConfig) 資源單元(Unit) 租戶(Tenant) OceanBase兼容了大部分MySQL連接協(xié)議和語法,租戶的使用體驗跟MySQL實例很像。研發(fā)可以在租戶里創(chuàng)建數(shù)據(jù)庫(Database)、表(Table)。還包括分區(qū)表等。 OceanBase里描述數(shù)據(jù)的最小粒度是分區(qū)。普通的表(非分區(qū)表)就是一個分區(qū),分區(qū)表則包含多個分區(qū)。 租戶的示意圖如下。租戶之間數(shù)據(jù)是絕對隔離,資源有一定程度隔離。研發(fā)可以將業(yè)務(wù)先垂直拆分為多個獨(dú)立的子業(yè)務(wù),分別使用不同的租戶或者集群。 圖2 OceanBase多租戶示意圖 OceanBase資源單元租戶里并不知道數(shù)據(jù)具體在哪個機(jī)器上,也可以說沒必要知道。只是租戶的性能還取決于運(yùn)維為租戶規(guī)劃的資源池分布情況,所以了解一下資源單元的分布特點(diǎn)對性能規(guī)劃也是有意義的。 資源池(Resource Pool)是由一組資源單元(Resource Unit)組成。資源單元數(shù)量默認(rèn)跟Zone的數(shù)量一致或者是它的倍數(shù)(可以配置具體分布在哪些Zone以及每個Zone里的Unit數(shù)量)。如下圖 圖 3 OceanBase 資源池分配示意圖 資源單元具備一定的資源能力,是數(shù)據(jù)的容器。租戶擁有的資源單元規(guī)格和數(shù)量決定了這個租戶最大性能。資源單元可以在同一個Zone的不同節(jié)點(diǎn)之間自由遷移,OceanBase借此來維持各個節(jié)點(diǎn)的資源利用率盡可能維持一個均衡狀態(tài)。 OceanBase拆分設(shè)計數(shù)據(jù)庫拆分數(shù)據(jù)庫拆分有兩種。 一是垂直拆分。即按業(yè)務(wù)模塊拆分到不同的實例或庫里。為了模塊之間互不影響,拆分到不同的實例比較好。在OceanBase里實現(xiàn)時可以是拆分到同一個集群里不同租戶或者不同集群里的租戶都可以,取決于業(yè)務(wù)規(guī)模和數(shù)據(jù)庫集群規(guī)模。垂直拆分很好理解,后面不再贅述。 一是水平拆分。即按某個業(yè)務(wù)維度將數(shù)據(jù)拆分到多個分片。這些分片可以是在一個庫或者不同庫或者不同實例的不同庫下。水平拆分實現(xiàn)又有兩類常用的選擇。如下:
水平拆分示意圖如下: 圖 4 水平拆分的分庫分表和分區(qū)表 上圖是分庫分表和分區(qū)表同時結(jié)合使用。業(yè)務(wù)表order先經(jīng)過中間件拆分為100個分表(存在10個分庫里),每個分表在OceanBase內(nèi)部又是一個分區(qū)表(100個分區(qū))。分庫分表的維度和分區(qū)表分區(qū)的維度都是一致的,根據(jù)用戶ID。 分庫分表和分區(qū)各有利弊。 分庫分表的好處是各個分表的結(jié)構(gòu)一致性是在中間件層保證,比較好控制,比較適合灰度變更(允許部分分表結(jié)構(gòu)不一致,最終必須全部一致)。此外更大的好處是,分庫分表是實現(xiàn)異地多活單元話架構(gòu)的必不可少的條件。缺點(diǎn)是中間件的SQL支持范圍有限。 分區(qū)的好處是在數(shù)據(jù)庫內(nèi)部解決了拆分問題。針對分區(qū)表的SQL功能是數(shù)據(jù)庫SQL引擎的本質(zhì)工作,相關(guān)特性(全局索引、二級分區(qū)等)會持續(xù)開發(fā)完善。 分區(qū)分庫分表架構(gòu)設(shè)計,需要確定機(jī)器數(shù)、實例數(shù)、分庫數(shù)和分表數(shù)的拓?fù)?,性能理論上限取決于主實例所處的機(jī)器節(jié)點(diǎn)數(shù)。此后要做擴(kuò)展就要調(diào)整這四個元素的數(shù)目及其聯(lián)系。這種擴(kuò)展很可能涉及到分表數(shù)據(jù)的遷移,需要借助外部工具或產(chǎn)品實現(xiàn)。 分區(qū)架構(gòu)設(shè)計,研發(fā)確定分區(qū)策略和分區(qū)數(shù),運(yùn)維確定租戶的資源單元數(shù)量,OceanBase確定資源單元(Unit)在哪些機(jī)器節(jié)點(diǎn)上以及分區(qū)(Partition)在哪些資源單元里。同一個分區(qū)不能跨節(jié)點(diǎn)存儲。如下圖。此后要做擴(kuò)展就是調(diào)整資源單元的規(guī)格、數(shù)量。 OceanBase在確定Unit里的分區(qū)的位置時會盡量讓每個節(jié)點(diǎn)的負(fù)載維持均衡。這個負(fù)載的計算方式比較復(fù)雜,會綜合考慮OB節(jié)點(diǎn)內(nèi)部CPU、內(nèi)存和空間利用率等。分區(qū)隨意分布對應(yīng)用性能很可能有負(fù)面影響。當(dāng)業(yè)務(wù)上有聯(lián)系的兩個表的分區(qū)分布在不同的資源單元里(同時也分布在不同的節(jié)點(diǎn)里),這兩個表的連接就難以避免跨節(jié)點(diǎn)請求數(shù)據(jù),網(wǎng)絡(luò)上的延時會影響這個連接的性能。 圖5 分區(qū)在資源單元中的位置 注: t1(p0) 表示表t1的0號分區(qū)。 每個分區(qū)在集群里數(shù)據(jù)實際有三份,即三副本(Replica)。圖中忽略了Zone2和Zone3的細(xì)節(jié)。三副本之間的數(shù)據(jù)同步靠把Leader副本的事務(wù)日志同步到其他Follower副本中。Paxos協(xié)議會保障這個事務(wù)日志傳輸?shù)目煽啃裕ㄊ聞?wù)日志在一半以上成員里落盤,剩余成員最終也會落盤),同時還有個以分區(qū)為粒度的選舉機(jī)制,保障Leader副本不可用的時候,能快速從現(xiàn)有兩個Follower副本里選舉出新的Leader副本,并且數(shù)據(jù)還絕對不丟。這里就體現(xiàn)了故障切換時兩個重要指標(biāo):RPO=0, RTO<30s。 Locality圖5中t0和t1業(yè)務(wù)上是有聯(lián)系的表(如主表和詳情表),兩者都是分區(qū)表,分區(qū)策略和分片數(shù)都相同,OceanBase提供了一個表屬性“表分組”(TableGroup)。設(shè)置為同一個表分組的不同表的分區(qū)數(shù)一定一樣,并且同號分區(qū)組成一個“分區(qū)分組”(PartitionGroup)。同一個分區(qū)分組的分區(qū)一定會分配在同一個資源單元(Unit)內(nèi)部(也就是會在同一個節(jié)點(diǎn)內(nèi)部),彼此的連接邏輯就避免了跨節(jié)點(diǎn)請求。另外一個效果是如果一個事務(wù)同時修改兩個有業(yè)務(wù)關(guān)聯(lián)的分區(qū),使用分區(qū)分組也可以規(guī)避跨節(jié)點(diǎn)的分布式事務(wù)。這個表分組屬性的設(shè)置就是OceanBase的Locality特性之一——影響相關(guān)分區(qū)的分布。 實際上每個分區(qū)都有三副本(Replica, 本文例子),圖5中省略了t0(p0)和t1(p0)的其他兩個副本都分別會在同一個Unit里分配。不僅如此,每個分區(qū)的三副本里都會有l(wèi)eader副本默認(rèn)提供讀寫服務(wù)。leader副本是選舉出來的。t0(p0)和t1(p0)的leader副本也一定會在同一個Unit里(即在同一個Zone里)。這樣才徹底的避免了連接的時候跨節(jié)點(diǎn)請求。 OceanBase的Locality特性還可以指定租戶/數(shù)據(jù)庫/表的默認(rèn)Zone,這樣下面的表的leader副本會優(yōu)先被選舉為這個Zone里副本。 如下面例子,數(shù)據(jù)庫和表會繼承租戶的默認(rèn)設(shè)置,當(dāng)然也可以自己修改primary_zone或者locality屬性覆蓋上層設(shè)置。: create tenant obtrans0primary_zone='hz1'; create table item (…)locality = 'F@hz1, F@hz2, F@hz3,R{all_server}@hz1, R{all_server}@hz2, R{all_server}@hz3' 注:F表示全功能副本,R表示只讀副本。 設(shè)置primary_zone后單個租戶的所有表的讀寫都集中到一個Zone里,該租戶在其他zone里的Unit就沒有讀寫壓力。通常這樣做是源于業(yè)務(wù)應(yīng)用的要求。如應(yīng)用的請求都是來自于這個Zone,為了規(guī)避應(yīng)用跨Zone讀寫數(shù)據(jù)性能下降。不過primary_zone更大的意義在于當(dāng)集群里有很多租戶的時候,可以將不同業(yè)務(wù)租戶的讀寫壓力分?jǐn)偟讲煌琙one的機(jī)器,這樣集群整體資源利用率提升,所有應(yīng)用的總體性能也得到提升。后面要介紹的異地多活架構(gòu)就充分利用OceanBase這個特性,在數(shù)據(jù)庫層面將拆分的表的業(yè)務(wù)讀寫點(diǎn)盡可能分散到所有Zone的所有機(jī)器上。 除了表與表之間可能有聯(lián)系,業(yè)務(wù)模塊之間也可能有聯(lián)系。一個業(yè)務(wù)過程可能會橫跨多個業(yè)務(wù)模塊,前面這些模塊的數(shù)據(jù)被垂直拆分到多個租戶里。OceanBase的Locality特性“租戶分組”(TenantGroup)還可以設(shè)置不同租戶之間的聯(lián)系。如下租戶交易訂單和支付訂單在業(yè)務(wù)上是先后發(fā)生。 create tenantgroup tgtrade tenant_array=('obtrade0', 'obpay0'); 租戶分組的意義依然是為了在分布式架構(gòu)下盡可能將一個業(yè)務(wù)流程內(nèi)多次數(shù)據(jù)庫請求都約束在同一個Zone或者Region(注:OceanBase將地域相鄰的Zone定義為一個Region)里。 OceanBase異地多活架構(gòu)異地多活概念異地多活的概念一直都有,只是內(nèi)涵不斷變化。以雙機(jī)房多活為例,應(yīng)用通常都是無狀態(tài)的,可以多地部署。數(shù)據(jù)庫有狀態(tài),傳統(tǒng)數(shù)據(jù)庫只有主庫可以提供讀寫,備庫最多只能提供只讀服務(wù)(如ORACLE的Active Dataguard):
上面第1種情形,B地應(yīng)用是跨地域遠(yuǎn)程讀寫數(shù)據(jù)庫。兩地距離較大的時候性能會很不好。2的B地應(yīng)用是本地訪問數(shù)據(jù)庫。3,4,5三種情形兩地數(shù)據(jù)庫都提供讀寫服務(wù),對應(yīng)用而言是本地訪問數(shù)據(jù)庫,但到分布式數(shù)據(jù)庫內(nèi)部,其要讀寫的數(shù)據(jù)是否正好在本地就取決于業(yè)務(wù)和數(shù)據(jù)庫的拆分設(shè)計。有這么一種情形,B地應(yīng)用訪問B地數(shù)據(jù)庫實例,請求的數(shù)據(jù)的寫入點(diǎn)恰好是A地,這樣會走分布式數(shù)據(jù)庫內(nèi)部路由遠(yuǎn)程A地實例中拿到數(shù)據(jù),性能上會有些下降,而業(yè)務(wù)可能不知道為什么。 OceanBase水平拆分方案為了避免在分布式數(shù)據(jù)庫OceanBase內(nèi)部發(fā)生跨Zone請求,應(yīng)用的請求流量的水平拆分規(guī)則和數(shù)據(jù)的水平拆分規(guī)則要保持一致并聯(lián)動,就可以實現(xiàn)真正的應(yīng)用向本地實例請求讀寫的數(shù)據(jù)恰好就是在本地實例種。這就是Locality的用途所在。 圖 6 OceanBase集群異地多活水平拆分示意圖 首先業(yè)務(wù)架構(gòu)層面能根據(jù)用戶ID(uid)做拆分,將用戶拆分為100分。x和y是用戶直接相關(guān)的表,都根據(jù)uid拆分為100個分表,分布在5個不同的租戶里。x[00-19]表示20個分表。每個租戶的Leader分別分布在不同的Zone。uid:00-19表示 分到這20個分片的用戶數(shù)據(jù)和用戶流量。 應(yīng)用層面在某個環(huán)節(jié)能根據(jù)UID將用戶請求轉(zhuǎn)發(fā)到對應(yīng)的機(jī)房(Zone),應(yīng)用服務(wù)之間的請求信息都有這個UID信息,后面應(yīng)用請求都在本機(jī)房流轉(zhuǎn),并且訪問數(shù)據(jù)庫時通過分庫分表中間件(DBP)和OceanBase的反向代理(OBProxy)就路由到本機(jī)房的業(yè)務(wù)租戶上。 實際使用這個架構(gòu)時,有幾個隱含的前提,Zone1和Zone2是同城雙機(jī)房,Zone3和Zone4是同城雙機(jī)房,兩個城市靠的比較近,Zone5 實際很遠(yuǎn),所以一般不提供寫入。為節(jié)省空間,Zone5里的副本放的是日志副本。 應(yīng)用異地多活架構(gòu)上面要實現(xiàn)OceanBase在每個Zone的應(yīng)用寫入都是恰好是本地訪問,關(guān)鍵點(diǎn)就是應(yīng)用流量水平拆分規(guī)則跟數(shù)據(jù)水平拆分規(guī)則保持一致。而應(yīng)用要維持這樣一個規(guī)則,需要很多產(chǎn)品都要支持水平拆分等。如下圖 圖 7 應(yīng)用三地五中心多活解決方案 圖中流量接入層的“負(fù)載均衡”環(huán)節(jié)負(fù)責(zé)調(diào)整用戶流量到對應(yīng)的機(jī)房(Zone),此后的應(yīng)用之間的請求就都是本地訪問。 能使用這個解決方案的業(yè)務(wù)都是能按用戶維度做水平拆分。有些業(yè)務(wù)的拆分維度跟用戶維度是沖突的,或者有些業(yè)務(wù)的數(shù)據(jù)只支持集中寫入等,不能做到上面這種多活,但也可以使用OceanBase,實現(xiàn)單點(diǎn)寫入,多點(diǎn)讀取的功能。 OceanBase在異地容災(zāi)和多活架構(gòu)方案中的價值就是支持水平拆分規(guī)則的定義、解決了多個機(jī)房間數(shù)據(jù)同步和一致性問題、始終具備高可用和彈性伸縮能力等。 |
|