OB君:做爲一款100%自研的分佈式數據庫,OceanBase歷經了近十年的發展歷程。近十年來,OceanBase的存儲架構經歷了屢次演進,以解決業務愈來愈複雜苛刻的存儲需求。本文整理自趙裕衆(花名陳羣)在2019 SACC中國系統架構師大會中的演講。
隨着用戶數據量的不斷增加,基於傳統共享存儲的縱向擴展能力漸漸變得力不從心,分佈式存儲成爲應對用戶海量數據的標配。算法
做爲一位架構師,在設計系統的分佈式存儲架構時,須要關注哪些方面呢?或者咱們換句話說,對於客戶來講,一個理想中的分佈式存儲產品應該具備哪些特性呢?數據庫
咱們認爲完善的分佈式存儲架構應該關注這五個方面:編程
架構設計服務於業務,再完美的系統架構都須要有業務進行使用才能創造價值。對於業務來講,固然但願咱們的產品可以同時具有高擴展、高可用、強一致、低成本以及零門檻的易用性,但對於系統架構師和開發者來講,這五個特性之間存在相互矛盾的很多,同時在實現這些特性時也會面臨巨大的複雜性,這要求咱們在作系統設計及實現時須要有所權衡。服務器
下面咱們就OceanBase創立九年多以來存儲架構的演進歷程,來回顧每一次架構變動背後的權衡與思考。架構
1)OceanBase 0.1版本(2010年)併發
OceanBase由陽振坤於2010年在淘寶創立,當時淘寶大多數業務都已經按照用戶維度作了分庫分表,一個全新的分佈式存儲系統彷佛很難有用武之地。最終咱們找到了OceanBase的第一個業務:淘寶收藏夾,也就是咱們今天打開手淘看到喜歡的商品點收藏時用到的收藏夾,直到今天它仍然跑在OceanBase數據庫上面。負載均衡
當時收藏夾面臨了一個分庫分表難以解決的問題,它的核心業務主要包括兩張表,一張是用戶表,記錄一個用戶收藏的商品條目,數量從幾條到幾千條不等;另外一張是商品表,記錄一件商品的描述、價格等明細信息。若是一個用戶增長/刪除收藏,那麼相應的就向用戶表中插入/刪除數據就能夠了;同時若是一個商家須要修改商品描述,例如修改商品價格等信息,那麼相應的更新商品表就能夠了。框架
當用戶打開收藏夾時,經過用戶表和商品表的鏈接查詢,就能夠展示給用戶最新的商品信息。最開始的時候,這兩張表是在一個數據庫裏面,也一直運行地很好,但隨着用戶數據量的增加,單個數據庫放不下了,通常經常使用的作法是將表按照用戶維度進行拆分,用戶表是能夠這樣拆,可是商品表中沒有用戶字段,若是按照商品條目進行拆分,那麼在用戶打開收藏夾時,就須要對多個不一樣的庫進行查詢並作鏈接,當時的數據庫中間件並無這樣的能力,即便能夠這麼作,一次查詢也會耗費很是長的時間,會極大的影響用戶體驗,業務遇到了很大的困難。運維
OceanBase接下了用戶的這個難題,若是咱們分析擴展性、高可用、一致性、低成本和易用性這五個特性,那麼什麼是業務的剛需,什麼是業務能夠放棄的呢?業務最強的剛需是擴展性,由於傳統的單機模式已經走到了盡頭;最能夠放棄的實際上是易用性,由於業務對寫入查詢的使用很是簡單,提供簡單的讀寫接口就能夠知足業務需求,業務甚至不須要在表上構建索引。同時咱們也注意到業務對一致性也有必定的需求,業務能夠容忍必定的弱一致讀,但不能容忍數據出錯。這些特性決定了OceanBase從誕生的第一天起,就是一個支持在線事務處理的關係型分佈式數據庫。異步
咱們注意到收藏夾這個業務的特性,它的存量數據比較大,可是天天的增量並不大,畢竟天天新增收藏的用戶並非特別多。它更關心數據存儲的擴展性,而對寫入的擴展性要求並非很高。咱們將數據分爲兩部分:基線數據和增量數據。基線數據是靜態的,分佈式地存儲在ChunkServer上。增量數據寫在UpdateServer上,一般是存儲在內存裏面,經過Redo Log支持在線事務,在天天的業務低峯期,UpdateServer上的數據會與ChunkServer上的數據作合併,咱們稱之爲「每日合併」。MergeServer是一個無狀態的Server,提供數據寫入的路由與數據查詢的歸併;RootServer負責整個集羣的調度與負載均衡。這是一個相似於LSM Tree的存儲架構,這也決定了從此OceanBase的存儲引擎都是基於LSM Tree的。
咱們回過頭來看OceanBase0.1的架構,它實際上具備很強的一致性,由於寫入是個單點,讀到的數據必定是最新寫入的數據,同時成本也並不高,也具備必定的擴展性,存儲空間能夠很容易地作擴展,很好知足了當時業務的需求。
2)OceanBase 0.2-0.3版本(2011年)
很快OceanBase 0.1版本上線了,併爲收藏夾業務提供了讀服務,但業務不能把全部流量都切到OceanBase上面來,由於OceanBase 0.1版本的架構有着一個很大的缺陷:它不是高可用的。任何一臺服務器的宕機都會形成數據的不可訪問,這對於收藏夾這樣的業務是沒法接受的。很快咱們帶來了OceanBase 0.2版本的架構,補上了高可用的短板。
在OceanBase 0.2版本中咱們引入了主備庫模式,這也是當時傳統數據庫經常使用的容災模式,數據經過redo log從主庫同步到備庫,當主庫發生問題時,能夠把備庫切換爲主庫繼續提供服務。redo log的同步是異步的,這意味着主備的切換是有損的,可能會丟失若干秒的數據。
咱們將OceanBase 0.2版本和OceanBase 0.1版本的架構進行對比,會發現OceanBase 0.2版本終於有了高可用這一重要特性,但高可用的得到不是沒有代價的,首先系統再也不是強一致的了,咱們不可以保證業務老是可以讀到最新的數據,在宕機場景下,數據可能會有部分丟失;其次主備庫的引入極大地增長了成本,咱們使用的機器數量翻番了。以後的OceanBase 0.3版本基於OceanBase 0.2版本作了不少代碼上的優化,進一步下降了系統成本,但從架構上來講和OceanBase 0.2版本並無顯著的差異。
3)OceanBase 0.4版本(2012年)
隨着收藏夾業務的成功,很快咱們接到了更多新的業務,淘寶直通車是一個面向商家的業務,也面臨着分庫分表難以解決的問題。首先淘寶直通車的數據量愈來愈大,單庫難以支撐,同時它又是一個OLAP類型的業務,有不少多表間的關聯查詢,每張表的維度又各不相同,沒法統一按照用戶id進行拆分。對於OceanBase的擴展性、高可用以及低成本業務都很滿意,可是接口使用確實是太痛苦了。那麼問題來了,什麼是最好的接口語言?對於編程來講,可能不一樣的語言都有不一樣的擁躉,但對於數據操做來講,咱們認爲SQL必定是最好的語言。對於簡單的KV查詢,你可能會以爲SQL過於沉重了,但當你的業務慢慢複雜起來後,SQL必定是使用最簡單輕便的。
在OceanBase 0.4版本,咱們對SQL有了初步的支持,用戶可使用標準SQL來訪問OceanBase,支持簡單的增刪改查以及關聯查詢,但對SQL的支持並不完整。同時OceanBase 0.4版本也是咱們最後一個開源版本。
對比OceanBase 0.4版本和OceanBase 0.2版本的架構,在OceanBase 0.4版本咱們最終補上了易用性的白板,開始慢慢有了一個標準分佈式數據庫的樣子。
4)OceanBase 0.5版本(2014年)
2012年末的時候,OceanBase團隊來到了支付寶。當時支付寶面臨着全面去掉IOE的強烈需求,IOE的成本過高了,但PC服務器的穩定性難以和高端存儲相比,若是咱們使用MySQL這樣的開源數據庫基於PC服務器進行替代,業務就面臨着可能會丟數據的潛在風險。當時基於MySQL的容災方案仍然只是主備同步,對於支付寶交易支付這樣的核心繫統來講,丟失一筆訂單形成的損失難以估量。業務對數據庫的強一致和高可用提出了更高的要求,也使得咱們搭建了OceanBase 0.5版本的新一代架構。
在OceanBase 0.5版本中,咱們引入了Paxos一致性協議,經過多數派選舉來保障單點故障下的數據一致性。通常狀況下,OceanBase 0.5版本的部署模式會是三副本,當有一個副本出現問題時,另外兩個副本會補齊日誌並從新選出一個主提供服務,咱們能夠作到單點故障下不丟失任何數據,同時故障恢復時間小於30s。同時爲了更好地支撐業務,在OceanBase 0.5版本中,咱們全面兼容了MySQL協議,支持了二級索引並有了基於規則的執行計劃,用戶能夠用MySQL的客戶端來無縫鏈接OceanBase,能夠像使用MySQL同樣來使用OceanBase。
對比OceanBase 0.4版本和OceanBase 0.5版本的架構,咱們發現OceanBase 0.5版本基於Paxos,有了更強的高可用以及強一致,基於SQL有了更好的易用性,但代價是從兩副本變成三副本,系統成本進一步增長了50%。
5)OceanBase 1.0版本(2016年)
在OceanBase 0.5版本的架構下,業務對強一致、高可用以及易用性的需求都獲得了很好的支持,痛點慢慢集中在擴展性和成本上。隨着用戶寫入量的不斷增加,UpdateServer的寫入單點老是會成爲瓶頸,同時三副本也帶來了太高的成本消耗。OceanBase 1.0版本帶來了全新的架構,重點解決了擴展性和成本的痛點。
在OceanBase 1.0版本中,咱們支持了多點寫入,從架構上將UpdateServer、ChunkServer、MergeServer和RootServer都合併爲一個OBServer,每個OBServer均可以承擔讀寫,總體架構更加優雅,運維部署也更加簡單。一張表能夠被劃分爲多個分區,不一樣分區能夠散佈在不一樣的OBServer上,用戶的讀寫請求經過一層代理OBProxy路由到具體的OBServer上進行執行。對於每一個分區都仍然經過Paxos協議作三副本高可用,當有一臺OBServer出現故障時,這臺OBServer上的分區會自動切到其餘包含對應分區的OBServer上提供服務。
在成本方面,咱們注意到在Paxos協議中,須要三副本同步的只是日誌,日誌須要寫三份,可是數據並非,和數據相比日誌量老是小的。若是將日誌和數據分開,咱們就可使用兩副本的存儲開銷實現三副本高可用。在OceanBase 1.0版本中,咱們將副本分爲兩種類型:全功能副本和日誌副本,其中全功能副本既包含數據也包含日誌,提供完整的用戶讀寫;日誌副本只包含日誌,只進行Paxos投票。
同時在OceanBase 1.0版本中,咱們引入了多租戶的概念,在同一個OceanBase集羣中,能夠支持多個不一樣的租戶,這些租戶共享整個集羣資源,OceanBase會對不一樣租戶的CPU、內存、IO及磁盤使用進行資源隔離。租戶能夠根據本身的須要配置不一樣的資源容量,集羣會根據不一樣OBServer的負載作動態的負載均衡。這使得咱們能夠把不少個小租戶部署到同一個大集羣中來,下降總體的系統成本。
和OceanBase 0.5版本相比,OceanBase 1.0版本的擴展性有了大幅提高,並且因爲引入了日誌副本和多租戶技術,在成本上有了大幅下降;但擴展性的提高不是沒有代價的,多點寫入帶來了巨大的複雜性。首先用戶的寫入並不必定會只寫單個分區,對於多個分區的寫入不可避免會帶來分佈式事務,咱們使用兩階段提交協議來完成分佈式事務。其次在一個分佈式系統中獲取一個全局單調遞增的時間戳是極其困難的,因爲沒有全局時鐘,咱們基於局部時間戳作單分區內的讀寫併發控制,這使得系統的一致性有了必定的限制,雖然單分區的查詢仍然是強一致的,但跨分區的查詢沒法保證讀的強一致,這對於用戶而言會是一個不小的限制。同時因爲分區的關係,二級索引成爲了分區內的局部索引,這要求索引鍵中必定須要包含分區鍵,沒法支持全局的惟一索引,這對於用戶使用也形成了必定的不便。
6)OceanBase 2.0版本(2018年)
OceanBase 2.0版本的外部總體架構與OceanBase 1.0版本沒有太大差異,仍然是一個share nothing的三副本架構,但在內部咱們對擴展性、高可用、一致性、低成本和易用性都作了極大的提高。
在擴展性方面,咱們實現了分區分裂的功能。在建表的時候,用戶對合適的分區數可能沒有很好的估計,當分區過大時,能夠經過分區分裂來使得分區數變多。儘管分區分裂是一個比較重的DDL操做,在OceanBase 2.0版本中,分區分裂是能夠在線進行的,對用戶的正常業務讀寫並不會形成太大影響。
在高可用方面,咱們支持了主備庫功能,對於某些只有雙機房的用戶,能夠在機房內經過三副本作機房內的無損容災,經過主備庫作跨機房的有損容災。
在一致性方面,咱們支持了全局快照,真正意義上實現了分佈式讀寫下的強一致。基於全局快照,咱們也完成了對全局索引以及外鍵的支持。
在低成本方面,咱們在事務層支持了TableGroup,容許把一組相近的表「綁定」在一塊兒,減小分佈式事務的開銷。在存儲層引入了數據編碼,經過字典、RLE、Const、差值、列間等值、列間前綴等算法進一步壓縮存儲空間的佔用,而且對於數據的編碼是自適應的,會根據數據特徵來自動選擇合適的編碼算法。
在易用性方面,咱們支持了Oracle租戶,容許用戶在同一套ObServer集羣中同時使用MySQL租戶與Oracle租戶,而且支持存儲過程、窗口函數、層次查詢、表達式索引、全文索引、ACS、SPM、回收站等功能。
儘管今天的OceanBase 2.0版本在擴展性、高可用、一致性、低成本以及易用性方面作到了更好的平衡,但這樣的架構並非一蹴而就的,從OceanBase 0.1版本到OceanBase 2.0版本的發展歷程來看,OceanBase的架構老是在一直進化,爲了可以更好地服務於業務,不少事物老是面臨着許多權衡取捨,一項特性的提高會以其餘特性的下降爲代價。架構的優化演進沒有終點,將來爲了更好知足業務的需求,OceanBase的架構還會不斷進行演化。
做者介紹:趙裕衆(花名陳羣),螞蟻金服高級技術專家,目前在OceanBase團隊負責存儲相關的開發工做。2010年於中國科學技術大學得到計算機博士學位,同年加入支付寶從事分佈式事務框架的研發,2013年加入OceanBase團隊。
本文做者:趙裕衆
本文爲雲棲社區原創內容,未經容許不得轉載。