UCloud分布式數(shù)據(jù)庫UDDB:技術(shù)實現(xiàn)
在此之前,我們介紹過UDDB的功能特性和產(chǎn)品理念,明確了產(chǎn)品的發(fā)展方向——基于數(shù)據(jù)庫中間件來做公有云上的分布式數(shù)據(jù)庫。在剛開始選擇較為簡單的工程實現(xiàn)復(fù)雜度,然后通過公有云這種互聯(lián)網(wǎng)服務(wù)的快速迭代能力和在線服務(wù)能力,不斷的提高對業(yè)務(wù)的支持度,從而覆蓋更多的業(yè)務(wù)。最終演進(jìn)為大數(shù)據(jù)時代的分布式數(shù)據(jù)庫解決方案,為互聯(lián)網(wǎng)、物聯(lián)網(wǎng)、傳統(tǒng)行業(yè)的轉(zhuǎn)型提供海量數(shù)據(jù)存儲、處理的在線服務(wù)。
1.UDDB的技術(shù)演進(jìn)路徑
雖然技術(shù)服務(wù)于產(chǎn)品,而產(chǎn)品的發(fā)展方向體現(xiàn)了根據(jù)客戶和市場所做的戰(zhàn)略思考,但是在執(zhí)行層面上,還只是一個模糊的目標(biāo)。因此,在技術(shù)實現(xiàn)上,需要把執(zhí)行的路徑想清楚,才能將目標(biāo)清晰地落地?;ヂ?lián)網(wǎng)創(chuàng)業(yè),強(qiáng)調(diào)的是樹目標(biāo)、定路徑、滾雪球式發(fā)展。其中,定路徑是關(guān)鍵的一環(huán):向上,必須與戰(zhàn)略規(guī)劃很好地對接;往前,每一步都要踩到點上。如此,才能在每一個階段創(chuàng)造價值,吸收資源,發(fā)展壯大。
執(zhí)行路徑的確定,需要回歸到客戶需求上,要深入到客戶業(yè)務(wù)中,去尋找規(guī)律和辦法。通過大量的調(diào)研,我們發(fā)現(xiàn)目前技術(shù)圈的熱點:超大表水平拆分問題,其實并不是客戶主要的痛點,讀寫分離和垂直分庫的需求,反而是沉默的大多數(shù)。從技術(shù)演進(jìn)的趨勢來看,水平拆分必然是分布式數(shù)據(jù)庫最終的目標(biāo)。但現(xiàn)階段水平拆分對SQL的支持度不高,導(dǎo)致為了做水平拆分,很多業(yè)務(wù)層必須要做脫胎換骨的改造。因此在數(shù)據(jù)量或性能要求還沒有到這個程度的情況下,客戶并不希望為了水平拆分做業(yè)務(wù)層的改造,而是傾向于更保守的讀寫分離和垂直分庫策略。UCloud技術(shù)團(tuán)隊基于客戶的這個訴求,最終確定了這樣一條技術(shù)演進(jìn)路徑:
- 基于數(shù)據(jù)庫中間件,用最短的時間做到一主多從讀寫分離場景下100% 兼容 MySQL,讓客戶的業(yè)務(wù)在庫表零變動、代碼零改動的情況下,使用上UDDB;
- 在垂直分庫場景下對MySQL的 100%兼容,讓一部分有垂直分庫需求的客戶,只需要調(diào)整好庫表位置,即可將業(yè)務(wù)接入UDDB;
- 逐步完善對水平拆分的支持,在產(chǎn)品推出初期,功能對齊業(yè)內(nèi)主流數(shù)據(jù)庫中間件。后續(xù)進(jìn)一步完善對SQL和事務(wù)的支持,結(jié)合對存儲系統(tǒng)(MySQL)的優(yōu)化、裁減或替換,實現(xiàn)最終的產(chǎn)品目標(biāo)。
在技術(shù)演進(jìn)上,三個階段并非割裂,每一個階段目標(biāo)的達(dá)成,都為下一個階段的目標(biāo)做好鋪墊。在讀寫分離和垂直分庫場景下,實現(xiàn)對MySQL的100%兼容,核心在于構(gòu)建一個完全對齊MySQL的語法解析器,解析完成后識別SQL的操作類型,即可進(jìn)行讀寫分離;識別SQL的作用對象(庫/表/視圖),即可在垂直分庫場景下,將SQL進(jìn)行有效路由和透傳;語法解析器的完善和成熟,又為水平拆分場景下,完善語義分析、分布式執(zhí)行計劃的生成、優(yōu)化和執(zhí)行打下了基礎(chǔ)。
總之,UDDB的最終目標(biāo)就是通過該技術(shù)演進(jìn)路徑,成為一款基于Shared-Nothing架構(gòu)的分布式數(shù)據(jù)庫。
在系統(tǒng)架構(gòu)和計算模型上,數(shù)據(jù)庫中間件+MySQL節(jié)點的分布式數(shù)據(jù)庫解決方案和NewSQL產(chǎn)品本質(zhì)上是一回事。限于本文的主題,關(guān)于數(shù)據(jù)庫中間件和NewSQL討論,在此不作展開。
2.UDDB的技術(shù)實現(xiàn)
技術(shù)路徑的確定是將產(chǎn)品目標(biāo)進(jìn)行落地,而技術(shù)實現(xiàn)則是將技術(shù)路徑進(jìn)行落地。到了技術(shù)實現(xiàn)的層面,重點有兩個:***是把基礎(chǔ)打好,讓產(chǎn)品的生長有一個牢靠的地基;第二是要進(jìn)行大膽創(chuàng)新,在時間和人力資源有限的情況下,通過靈活巧妙的辦法滿足客戶的需求或解決客戶的問題。
2.1UDDB的系統(tǒng)架構(gòu)
如圖所示UDDB的整個架構(gòu)主要有三大模塊:
- UDB資源池:等同于UDB產(chǎn)品的資源池, UDDB存儲節(jié)點和只讀實例直接復(fù)用處于同一可用區(qū)的UDB資源。
- 分布式數(shù)據(jù)庫中間件系統(tǒng):基于數(shù)據(jù)庫中間件技術(shù)構(gòu)建的分布式、多租戶數(shù)據(jù)庫中間件系統(tǒng)由以下幾個模塊構(gòu)成:Routerd、 Mgrd、高可用UDB實例(負(fù)責(zé)元數(shù)據(jù)和配置存儲)、Zookeeper集群(負(fù)責(zé)決策和調(diào)度)。
- UDDB管理控制臺:提供 UDDB 創(chuàng)建、管理、釋放等操作的 Web 界面。
我們可以從以下兩個方面理解該系統(tǒng)架構(gòu):
① 從左往右,以業(yè)務(wù)訪問的視角來看待該架構(gòu),可以看到:
- 客戶可以通過標(biāo)準(zhǔn)的MySQL API或者客戶端來訪問UDDB, 客戶的請求均發(fā)往ULB,由ULB轉(zhuǎn)發(fā)到某個中間件節(jié)點的Routerd模塊。
- Routerd模塊將對客戶請求進(jìn)行分析:
如果是DML請求,則在進(jìn)行處理后,直接轉(zhuǎn)發(fā)到相關(guān)的UDB節(jié)點,然后將各UDB節(jié)點的返回結(jié)果進(jìn)行聚合并返回給客戶端;
如果是 DDL請求,則通過Zookeeper集群,將該DDL任務(wù)通知到Mgrd模塊。 由Mgrd模塊將該 DDL任務(wù)取出、處理并廣播到UDDB下所有UDB節(jié)點。廣播完成后,將返回結(jié)果按原路經(jīng)Zookeeper集群遞交到Routerd ,***返回給客戶端。
②從右往左,以系統(tǒng)管理的視角來看待該架構(gòu),可以看到:UDDB的創(chuàng)建過程基本等同于目前利用數(shù)據(jù)庫中間件軟件+MySQL實例+負(fù)載均衡組件來搭建一個分布式數(shù)據(jù)庫解決方案的過程。這個過程為眾多開發(fā)團(tuán)隊的研發(fā)或者DBA所熟悉, 而UDDB的管理平臺無非是對這些流程做了一個完整的封裝,把這些煩瑣的操作替換為點擊鼠標(biāo)即可搞定。
2.2SQL解析和路由模塊
UDDB 在架構(gòu)上注重穩(wěn)健務(wù)實,而在SQL的解析和路由模塊(Routerd)的設(shè)計和實現(xiàn)上,則注重規(guī)范和專業(yè)。
Routerd的核心是一個SQL解釋器。它接收SQL語句,解析其語法和語義,確定該SQL影響哪些UDB節(jié)點,然后將SQL轉(zhuǎn)換成子SQL并下推到相關(guān)UDB節(jié)點。待UDB節(jié)點將結(jié)果返回后,可能需要根據(jù)原始SQL的語義將結(jié)果進(jìn)行過濾聚合,最終返回到客戶端。
該 SQL 解釋器的完善程度是 Routerd 的一個重要設(shè)計指標(biāo)。SQL 解釋器越完善,則對業(yè)務(wù)的支持越好,能夠支持的客戶端就越多,從而具備更好的通用性。公有云產(chǎn)品不能限制客戶類型和使用場景,因此通用性是非常重要的指標(biāo)。
業(yè)內(nèi)不少歷史悠久的數(shù)據(jù)庫中間件,雖然穩(wěn)定可靠支撐了不少實際項目,但是其SQL解釋器,卻一直做得不夠好??疾鞓I(yè)內(nèi)各種數(shù)據(jù)庫中間件的源碼實現(xiàn),我們可以看到, 不少中間件的實現(xiàn)存在兩個問題:
①有的中間件沒有獨立的SQL語法解析模塊,而是直接復(fù)用其他數(shù)據(jù)庫(如SqlLite)的語法解析器,或者開源SQL解析庫(如alibaba druid)。短期內(nèi),這種做法的確能夠讓項目迅速得以推進(jìn),但是后續(xù)功能的擴(kuò)展卻往往受制于該SQL語法解析器,因此不利于產(chǎn)品的長期發(fā)展。
②有的數(shù)據(jù)庫中間件有獨立、規(guī)范的語法解析器,但是在語義解析上做的不夠?qū)I(yè),這些中間件一般的解析流程如圖:
這樣雖然也能夠讓中間件工作,但是SQL的生成和結(jié)果的過濾聚合,都依賴抽象語法樹(AST)來完成。然而AST結(jié)構(gòu)復(fù)雜,攜帶信息也有限,使用AST來做SQL的生成和結(jié)果的過濾聚合,一方面會帶來編程上的復(fù)雜度;另一方面也不能執(zhí)行一些復(fù)雜的操作,比如 group by、order by、distinct、limit和集函數(shù)同時存在的SQL語句的聚合操作,因此很難實現(xiàn)通用性和可擴(kuò)展性。
如何做好Routerd的通用性? 可以從兩個方面著手:
***是基于Lex&Yacc構(gòu)建一個獨立、規(guī)范的語法解析模塊。讓研發(fā)團(tuán)隊做到對SQL語法圖和SQL語法解析實現(xiàn),做到了如指掌。這樣才能在有Bug時迅速修復(fù),需要添加新功能時能夠立即支持。
第二是采用類似數(shù)據(jù)庫系統(tǒng)實現(xiàn)的方式,來實現(xiàn)Routerd的語義解析。如大家所知,通用的數(shù)據(jù)庫系統(tǒng),執(zhí)行一條SQL有規(guī)范的流程如圖:
流程可以概括為語法解析、生成執(zhí)行計劃、執(zhí)行計劃優(yōu)化(查詢優(yōu)化)和執(zhí)行4個步驟。每一個步驟能夠很好地解耦,步驟之間通過約定的數(shù)據(jù)結(jié)構(gòu)來交互。這些數(shù)據(jù)結(jié)構(gòu)中,執(zhí)行計劃是最核心的,它詳細(xì)描述了SQL的語義、涉及到哪些數(shù)據(jù)庫內(nèi)部對象以及對這些內(nèi)部對象操作的順序。
參考數(shù)據(jù)庫系統(tǒng)的SQL解釋流程,UDDB的Routerd模塊的流程是:
Routerd中的分布式執(zhí)行計劃:一方面是對抽象與語法樹結(jié)構(gòu)更加精簡、扁平的描述,讓子SQL的生成更方便;另一方面,加入SQL結(jié)果過濾聚合的控制信息,方便對UDB節(jié)點返回結(jié)果的提取、過濾和聚合。
經(jīng)過一年多的演進(jìn)和迭代,UDDB的分布式執(zhí)行計劃和計劃執(zhí)行在逐步完善。從最初實現(xiàn)對單表SQL,以及落到同一節(jié)點 的JOIN SQL的100%支持,到支持多表跨節(jié)點Join、分布式事務(wù)這兩個核心功能(分布式事務(wù)功能目前內(nèi)測中,跨節(jié)點JOIN計劃于2018年上半年推出)。實踐證明,通過合理的架構(gòu),能夠讓UDDB從一款簡單的中間件出發(fā),走向更開闊的未來,成為基于MySQL并保留MySQL原生部署和運維體驗的,真正的分布式數(shù)據(jù)庫。同時,引入和數(shù)據(jù)庫內(nèi)核同樣的架構(gòu),這意味著還可以添加執(zhí)行計劃優(yōu)化的環(huán)節(jié),對分布式執(zhí)行計劃進(jìn)行優(yōu)化,最終不僅在功能上對齊單機(jī)數(shù)據(jù)庫,在性能上也有不斷優(yōu)化的空間。
2.3讀寫分離模式100%兼容MySQL
接下來我們將給出讀寫分離100%兼容MySQL的一個創(chuàng)新性的技術(shù)實現(xiàn)供業(yè)內(nèi)參考。垂直分庫實現(xiàn)對MySQL DDL、DML語法的100%支持,其原理也類似,在本文中不做贅述。
如果做一款單純的讀寫分離中間件, 在這款中間件中做到100%SQL兼容并不難。 只需要對SQL做輕量的解析,識別SQL的是讀SQL還是寫SQL,然后使用透傳的方法透傳到主從節(jié)點即可:
作為一種描述性語言,SQL的語法有一個非常明顯的規(guī)律:最前面的單詞必然是操作行為(動詞), 后面是操作對象(名詞)和限制條件。而從操作行為即可完全判斷出該SQL的讀/寫類型(call 存儲過程除外,因為存儲過程可寫可讀)。因此只需要提取前面幾個單詞,即可做正確的路由。
通過這兩個流程可以看到讀寫分離和水平分表兩種模式,在技術(shù)實現(xiàn)上的一個矛盾:讀寫分離假定所有的表都是不拆分的普通表, 需要提取SQL中的動作語義來識別讀寫SQL,繼而將SQL進(jìn)行透傳; 而水平分表模式下,則需要提取SQL中的作用對象,識別到底是哪幾張表,然后進(jìn)行表名的改寫(必要時也進(jìn)行其他子句的改寫)。兩者提取信息的邏輯沒有交集,導(dǎo)致兩種模式無法有機(jī)結(jié)合。
2.4UDDB的技術(shù)創(chuàng)新
1. 修改語法解析模塊:在解析SQL生成抽象語法樹的同時,將SQL中的庫表名稱提出到一個鏈表中。 假如語法解析器足夠規(guī)范,那么必然會有一個或幾個非終結(jié)符用于歸約SQL中的庫表名稱。此時,可以在這些非終結(jié)符對應(yīng)的語義動作代碼中,增加將庫表名稱保存到鏈表的操作:
2. 在語法解析之后獲得抽象語法樹以及鏈表,然后掃描鏈表,依次取出該SQL涉及的庫表名稱,結(jié)合中間件的元數(shù)據(jù)信息,判斷這些表是普通表還是水平分表。如果都是普通表,則將該SQL按照讀寫類型透傳到主節(jié)點或從節(jié)點;如果是水平分表,則再進(jìn)行語義分析、執(zhí)行計劃生成和計劃執(zhí)行。
通過以上兩步,做到讀寫分離模式下,SQL接近100%的兼容以及讀寫分離模式和水平分表模式在一個產(chǎn)品中***共存。 這其中的關(guān)鍵點在于語法解析模塊:需要實現(xiàn)一個規(guī)范完整并且能夠和MySQL官方對齊的語法解析模塊,有了該模塊即可做到對所有SQL都能夠進(jìn)行語法解析,在解析過程中進(jìn)行庫表提?。煌瑫r, 需要精心設(shè)計該語法解析模塊,將所有SQL的庫表子句,抽象為特定的幾個非終結(jié)符,從而方便植入庫表提取代碼。
該方法的優(yōu)點在于性能和實現(xiàn)上的簡單:庫表的提取,充分復(fù)用了語法解析過程,沒有額外的開銷;庫表類型(普通表/水平拆分的大表)的判斷,則只需要掃描提取出的庫表鏈表即可完成,性能開銷幾乎可以忽略不計;實現(xiàn)上也非常簡單,總共不超過150行代碼。
3.結(jié)語
本文介紹了UDDB的技術(shù)演進(jìn)路徑及其背后的系統(tǒng)架構(gòu)技術(shù)實現(xiàn)原理,以幫助客戶解決單機(jī)MySQL中的問題、讓客戶業(yè)務(wù)運行更順暢為宗旨。結(jié)合UCloud高水平的數(shù)據(jù)庫內(nèi)核開發(fā)能力,打造了一個結(jié)構(gòu)規(guī)范、實現(xiàn)工整的數(shù)據(jù)庫中間件,具備獨立完整的語法解析、語義解析、執(zhí)行計劃生成和計劃執(zhí)行模塊;我們不斷解決MySQL的兼容性問題,目前已經(jīng)支持所有的MySQL客戶端管理工具,包括PhpMyAdmin、Navicat、SequelPro等。2018年上半年,我們將實現(xiàn)對分布式事務(wù)和分布式Join的原生支持,從而完成對存儲、事務(wù)以及SQL執(zhí)行三大塊的分布式化,最終成為既保留MySQL原生部署和運維體驗,又徹底解決單機(jī)MYSQL容量和性能問題的真正的分布式數(shù)據(jù)庫。









































