首度公开!OceanBase存储系统架构的演进历程及工程实践
隨著用戶數據量的不斷增長,基于傳統(tǒng)共享存儲的縱向擴展能力漸漸變得力不從心,分布式存儲成為應對用戶海量數據的標配。
作為一位架構師,在設計系統(tǒng)的分布式存儲架構時,需要關注哪些方面呢?或者我們換句話說,對于客戶來說,一個理想中的分布式存儲產品應該具有哪些特性呢?
我們認為完善的分布式存儲架構應該關注這五個方面:
- 擴展性:擴展性可謂是分布式存儲有別于單機存儲的重要特征,分布式存儲的擴展性遠遠好于單機存儲,但是不同分布式存儲系統(tǒng)的擴展性之間仍然有著非常大的差異。某些分布式系統(tǒng)在幾十個節(jié)點的量級上可能運行的很好,但是節(jié)點如果再多下去,到了上百個乃至上千個的時候可能就會面臨很大的問題。用戶數據的增長沒有止境,如果不能做到線性擴展,那么今天能夠支撐業(yè)務的系統(tǒng)到了明天可能就會成為業(yè)務進一步發(fā)展的阻礙。
- 高可用:在分布式系統(tǒng)中出現節(jié)點故障是很常見的事情,分布式系統(tǒng)中的節(jié)點越多,出現節(jié)點故障的頻率也就會越高。在發(fā)生節(jié)點故障時是否能夠保證系統(tǒng)仍然可用,對于很多業(yè)務來說至關重要。根據故障類型和恢復時間的不同,高可用也分有不同的級別,系統(tǒng)是否可以容單點故障、多點故障、單機房故障、多機房故障、單城市故障、多城市故障?系統(tǒng)恢復是否可以做到天級恢復、小時級恢復、分鐘級恢復、秒級恢復?不同業(yè)務場景可能會對高可用有不同的要求。
- 一致性:一致性其實是一個被濫用的概念,很多同學會將其和數據庫事務特性中ACID中的C相混淆,在這里我們特指分布式系統(tǒng)的一致性。那么分布式系統(tǒng)的一致性指的是什么?如果用一句話進行概括,我們認為一致性指的是在一個分布式的讀寫系統(tǒng)中,用戶是否總能讀到最新寫入的數據。如果總是能讀到,那么這個系統(tǒng)就是強一致的,否則則是弱一致的。最終一致性則是弱一致的一種特例,指的是盡管不能總是讀到最新的數據,但隨著寫入操作的終止,最終還是可以讀到最新的數據。盡管很多分布式系統(tǒng)都宣稱提供一致性,但很多時候它們只是提供弱一致或者最終一致。強一致對于一些業(yè)務特別是和交易相關的金融業(yè)務來說至關重要,如果不能保證總是讀到最新的數據,那么就會有發(fā)生資損的可能。
- 低成本:分布式存儲系統(tǒng)可以使用更加廉價的PC服務器來代替高端的小機及大機,在成本上具有顯著的優(yōu)勢。但低成本并不意味著低性能,事實上由于分布式系統(tǒng)下有著非常多的節(jié)點,同時使用這些節(jié)點的能力可以為我們帶來相比大型服務器更高的性能。低成本高性能可以為我們的用戶節(jié)省更多的系統(tǒng)成本。
- 易用性:低成本特性通常關注于硬件成本,易用性則關系到人力成本。對于開發(fā)同學來說,易用性意味著簡單易上手的使用接口,最好學習和遷移成本為零,同時功能強大,能夠滿足各式各樣的需求;對于運維同學來說,易用性則意味著系統(tǒng)穩(wěn)定健壯,系統(tǒng)監(jiān)控及運維手段完善,學習及使用門檻低。
架構演進歷程
架構設計服務于業(yè)務,再完美的系統(tǒng)架構都需要有業(yè)務進行使用才能創(chuàng)造價值。對于業(yè)務來說,當然希望我們的產品能夠同時具備高擴展、高可用、強一致、低成本以及零門檻的易用性,但對于系統(tǒng)架構師和開發(fā)者來說,這五個特性之間存在相互矛盾的不少,同時在實現這些特性時也會面臨巨大的復雜性,這要求我們在做系統(tǒng)設計及實現時需要有所權衡。
下面我們就OceanBase創(chuàng)立九年多以來存儲架構的演進歷程,來回顧每一次架構變更背后的權衡與思考。
1)OceanBase 0.1版本(2010年)
OceanBase由陽振坤于2010年在淘寶創(chuàng)立,當時淘寶大多數業(yè)務都已經按照用戶維度做了分庫分表,一個全新的分布式存儲系統(tǒng)似乎很難有用武之地。最終我們找到了OceanBase的第一個業(yè)務:淘寶收藏夾,也就是我們今天打開手淘看到喜歡的商品點收藏時用到的收藏夾,直到今天它仍然跑在OceanBase數據庫上面。
當時收藏夾面臨了一個分庫分表難以解決的問題,它的核心業(yè)務主要包括兩張表,一張是用戶表,記錄一個用戶收藏的商品條目,數量從幾條到幾千條不等;另一張是商品表,記錄一件商品的描述、價格等明細信息。如果一個用戶增加/刪除收藏,那么相應的就向用戶表中插入/刪除數據就可以了;同時如果一個商家需要修改商品描述,例如修改商品價格等信息,那么相應的更新商品表就可以了。
當用戶打開收藏夾時,通過用戶表和商品表的連接查詢,就可以展現給用戶最新的商品信息。最開始的時候,這兩張表是在一個數據庫里面,也一直運行地很好,但隨著用戶數據量的增長,單個數據庫放不下了,一般常用的做法是將表按照用戶維度進行拆分,用戶表是可以這樣拆,但是商品表中沒有用戶字段,如果按照商品條目進行拆分,那么在用戶打開收藏夾時,就需要對多個不同的庫進行查詢并做連接,當時的數據庫中間件并沒有這樣的能力,即使可以這么做,一次查詢也會耗費非常長的時間,會極大的影響用戶體驗,業(yè)務遇到了很大的困難。
OceanBase接下了用戶的這個難題,如果我們分析擴展性、高可用、一致性、低成本和易用性這五個特性,那么什么是業(yè)務的剛需,什么是業(yè)務可以放棄的呢?業(yè)務最強的剛需是擴展性,因為傳統(tǒng)的單機模式已經走到了盡頭;最可以放棄的其實是易用性,因為業(yè)務對寫入查詢的使用非常簡單,提供簡單的讀寫接口就可以滿足業(yè)務需求,業(yè)務甚至不需要在表上構建索引。同時我們也注意到業(yè)務對一致性也有一定的需求,業(yè)務可以容忍一定的弱一致讀,但不能容忍數據出錯。這些特性決定了OceanBase從誕生的第一天起,就是一個支持在線事務處理的關系型分布式數據庫。
我們注意到收藏夾這個業(yè)務的特性,它的存量數據比較大,但是每天的增量并不大,畢竟每天新增收藏的用戶并不是特別多。它更關心數據存儲的擴展性,而對寫入的擴展性要求并不是很高。我們將數據分為兩部分:基線數據和增量數據?;€數據是靜態(tài)的,分布式地存儲在ChunkServer上。增量數據寫在UpdateServer上,通常是存儲在內存里面,通過Redo Log支持在線事務,在每天的業(yè)務低峰期,UpdateServer上的數據會與ChunkServer上的數據做合并,我們稱之為“每日合并”。MergeServer是一個無狀態(tài)的Server,提供數據寫入的路由與數據查詢的歸并;RootServer負責整個集群的調度與負載均衡。這是一個類似于LSM Tree的存儲架構,這也決定了今后OceanBase的存儲引擎都是基于LSM Tree的。
我們回過頭來看OceanBase0.1的架構,它實際上具有很強的一致性,因為寫入是個單點,讀到的數據一定是最新寫入的數據,同時成本也并不高,也具有一定的擴展性,存儲空間可以很容易地做擴展,很好滿足了當時業(yè)務的需求。
2)OceanBase 0.2-0.3版本(2011年)
很快OceanBase 0.1版本上線了,并為收藏夾業(yè)務提供了讀服務,但業(yè)務不能把所有流量都切到OceanBase上面來,因為OceanBase 0.1版本的架構有著一個很大的缺陷:它不是高可用的。任何一臺服務器的宕機都會造成數據的不可訪問,這對于收藏夾這樣的業(yè)務是無法接受的。很快我們帶來了OceanBase 0.2版本的架構,補上了高可用的短板。
在OceanBase 0.2版本中我們引入了主備庫模式,這也是當時傳統(tǒng)數據庫常用的容災模式,數據通過redo log從主庫同步到備庫,當主庫發(fā)生問題時,可以把備庫切換為主庫繼續(xù)提供服務。redo log的同步是異步的,這意味著主備的切換是有損的,可能會丟失若干秒的數據。
我們將OceanBase 0.2版本和OceanBase 0.1版本的架構進行對比,會發(fā)現OceanBase 0.2版本終于有了高可用這一重要特性,但高可用的獲得不是沒有代價的,首先系統(tǒng)不再是強一致的了,我們不能夠保證業(yè)務總是能夠讀到最新的數據,在宕機場景下,數據可能會有部分丟失;其次主備庫的引入極大地增加了成本,我們使用的機器數量翻番了。之后的OceanBase 0.3版本基于OceanBase 0.2版本做了很多代碼上的優(yōu)化,進一步降低了系統(tǒng)成本,但從架構上來說和OceanBase 0.2版本并沒有顯著的差別。
3)OceanBase 0.4版本(2012年)
隨著收藏夾業(yè)務的成功,很快我們接到了更多新的業(yè)務,淘寶直通車是一個面向商家的業(yè)務,也面臨著分庫分表難以解決的問題。首先淘寶直通車的數據量越來越大,單庫難以支撐,同時它又是一個OLAP類型的業(yè)務,有很多多表間的關聯(lián)查詢,每張表的維度又各不相同,無法統(tǒng)一按照用戶id進行拆分。對于OceanBase的擴展性、高可用以及低成本業(yè)務都很滿意,但是接口使用確實是太痛苦了。那么問題來了,什么是最好的接口語言?對于編程來說,可能不同的語言都有不同的擁躉,但對于數據操作來說,我們認為SQL一定是最好的語言。對于簡單的KV查詢,你可能會覺得SQL過于沉重了,但當你的業(yè)務慢慢復雜起來后,SQL一定是使用最簡單輕便的。
在OceanBase 0.4版本,我們對SQL有了初步的支持,用戶可以使用標準SQL來訪問OceanBase,支持簡單的增刪改查以及關聯(lián)查詢,但對SQL的支持并不完整。同時OceanBase 0.4版本也是我們最后一個開源版本。
對比OceanBase 0.4版本和OceanBase 0.2版本的架構,在OceanBase 0.4版本我們最終補上了易用性的白板,開始慢慢有了一個標準分布式數據庫的樣子。
4)OceanBase 0.5版本(2014年)
2012年底的時候,OceanBase團隊來到了支付寶。當時支付寶面臨著全面去掉IOE的強烈需求,IOE的成本太高了,但PC服務器的穩(wěn)定性難以和高端存儲相比,如果我們使用MySQL這樣的開源數據庫基于PC服務器進行替代,業(yè)務就面臨著可能會丟數據的潛在風險。當時基于MySQL的容災方案仍然只是主備同步,對于支付寶交易支付這樣的核心系統(tǒng)來說,丟失一筆訂單造成的損失難以估量。業(yè)務對數據庫的強一致和高可用提出了更高的要求,也使得我們搭建了OceanBase 0.5版本的新一代架構。
在OceanBase 0.5版本中,我們引入了Paxos一致性協(xié)議,通過多數派選舉來保障單點故障下的數據一致性。一般情況下,OceanBase 0.5版本的部署模式會是三副本,當有一個副本出現問題時,另外兩個副本會補齊日志并重新選出一個主提供服務,我們可以做到單點故障下不丟失任何數據,同時故障恢復時間小于30s。同時為了更好地支撐業(yè)務,在OceanBase 0.5版本中,我們全面兼容了MySQL協(xié)議,支持了二級索引并有了基于規(guī)則的執(zhí)行計劃,用戶可以用MySQL的客戶端來無縫連接OceanBase,可以像使用MySQL一樣來使用OceanBase。
對比OceanBase 0.4版本和OceanBase 0.5版本的架構,我們發(fā)現OceanBase 0.5版本基于Paxos,有了更強的高可用以及強一致,基于SQL有了更好的易用性,但代價是從兩副本變成三副本,系統(tǒng)成本進一步增加了50%。
5)OceanBase 1.0版本(2016年)
在OceanBase 0.5版本的架構下,業(yè)務對強一致、高可用以及易用性的需求都得到了很好的支持,痛點慢慢集中在擴展性和成本上。隨著用戶寫入量的不斷增長,UpdateServer的寫入單點總是會成為瓶頸,同時三副本也帶來了過高的成本消耗。OceanBase 1.0版本帶來了全新的架構,重點解決了擴展性和成本的痛點。
在OceanBase 1.0版本中,我們支持了多點寫入,從架構上將UpdateServer、ChunkServer、MergeServer和RootServer都合并為一個OBServer,每一個OBServer都可以承擔讀寫,整體架構更加優(yōu)雅,運維部署也更加簡單。一張表可以被劃分為多個分區(qū),不同分區(qū)可以散布在不同的OBServer上,用戶的讀寫請求通過一層代理OBProxy路由到具體的OBServer上進行執(zhí)行。對于每個分區(qū)都仍然通過Paxos協(xié)議做三副本高可用,當有一臺OBServer出現故障時,這臺OBServer上的分區(qū)會自動切到其他包含對應分區(qū)的OBServer上提供服務。
在成本方面,我們注意到在Paxos協(xié)議中,需要三副本同步的只是日志,日志需要寫三份,但是數據并不是,和數據相比日志量總是小的。如果將日志和數據分開,我們就可以使用兩副本的存儲開銷實現三副本高可用。在OceanBase 1.0版本中,我們將副本分為兩種類型:全功能副本和日志副本,其中全功能副本既包含數據也包含日志,提供完整的用戶讀寫;日志副本只包含日志,只進行Paxos投票。
同時在OceanBase 1.0版本中,我們引入了多租戶的概念,在同一個OceanBase集群中,可以支持多個不同的租戶,這些租戶共享整個集群資源,OceanBase會對不同租戶的CPU、內存、IO及磁盤使用進行資源隔離。租戶可以根據自己的需要配置不同的資源容量,集群會根據不同OBServer的負載做動態(tài)的負載均衡。這使得我們可以把很多個小租戶部署到同一個大集群中來,降低整體的系統(tǒng)成本。
和OceanBase 0.5版本相比,OceanBase 1.0版本的擴展性有了大幅提升,而且由于引入了日志副本和多租戶技術,在成本上有了大幅降低;但擴展性的提升不是沒有代價的,多點寫入帶來了巨大的復雜性。首先用戶的寫入并不一定會只寫單個分區(qū),對于多個分區(qū)的寫入不可避免會帶來分布式事務,我們使用兩階段提交協(xié)議來完成分布式事務。其次在一個分布式系統(tǒng)中獲取一個全局單調遞增的時間戳是極其困難的,由于沒有全局時鐘,我們基于局部時間戳做單分區(qū)內的讀寫并發(fā)控制,這使得系統(tǒng)的一致性有了一定的限制,雖然單分區(qū)的查詢仍然是強一致的,但跨分區(qū)的查詢無法保證讀的強一致,這對于用戶而言會是一個不小的限制。同時由于分區(qū)的關系,二級索引成為了分區(qū)內的局部索引,這要求索引鍵中一定需要包含分區(qū)鍵,無法支持全局的唯一索引,這對于用戶使用也造成了一定的不便。
6)OceanBase 2.0版本(2018年)
OceanBase 2.0版本的外部整體架構與OceanBase 1.0版本沒有太大差別,仍然是一個share nothing的三副本架構,但在內部我們對擴展性、高可用、一致性、低成本和易用性都做了極大的提升。
在擴展性方面,我們實現了分區(qū)分裂的功能。在建表的時候,用戶對合適的分區(qū)數可能沒有很好的估計,當分區(qū)過大時,可以通過分區(qū)分裂來使得分區(qū)數變多。盡管分區(qū)分裂是一個比較重的DDL操作,在OceanBase 2.0版本中,分區(qū)分裂是可以在線進行的,對用戶的正常業(yè)務讀寫并不會造成太大影響。
在高可用方面,我們支持了主備庫功能,對于某些只有雙機房的用戶,可以在機房內通過三副本做機房內的無損容災,通過主備庫做跨機房的有損容災。
在一致性方面,我們支持了全局快照,真正意義上實現了分布式讀寫下的強一致?;谌挚煺?#xff0c;我們也完成了對全局索引以及外鍵的支持。
在低成本方面,我們在事務層支持了TableGroup,允許把一組相近的表“綁定”在一起,減少分布式事務的開銷。在存儲層引入了數據編碼,通過字典、RLE、Const、差值、列間等值、列間前綴等算法進一步壓縮存儲空間的占用,并且對于數據的編碼是自適應的,會根據數據特征來自動選擇合適的編碼算法。
在易用性方面,我們支持了Oracle租戶,允許用戶在同一套ObServer集群中同時使用MySQL租戶與Oracle租戶,并且支持存儲過程、窗口函數、層次查詢、表達式索引、全文索引、ACS、SPM、回收站等功能。
總結
盡管今天的OceanBase 2.0版本在擴展性、高可用、一致性、低成本以及易用性方面做到了更好的平衡,但這樣的架構并不是一蹴而就的,從OceanBase 0.1版本到OceanBase 2.0版本的發(fā)展歷程來看,OceanBase的架構總是在一直進化,為了能夠更好地服務于業(yè)務,很多事物總是面臨著許多權衡取舍,一項特性的提升會以其他特性的降低為代價。架構的優(yōu)化演進沒有終點,未來為了更好滿足業(yè)務的需求,OceanBase的架構還會不斷進行演化。
原文鏈接
本文為云棲社區(qū)原創(chuàng)內容,未經允許不得轉載。
總結
以上是生活随笔為你收集整理的首度公开!OceanBase存储系统架构的演进历程及工程实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 支付宝双11狂欢幕后的女程序员:服务全球
- 下一篇: 【从入门到放弃-Java】并发编程-NI