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