阿里巴巴分布式數(shù)據(jù)庫服務DRDS研發(fā)歷程
淘寶TDDL研發(fā)歷史和背景
淘寶DRDS/TDDL是阿里巴巴自主研發(fā)的分布式數(shù)據(jù)庫服務。DRDS脫胎于阿里巴巴開源的Cobar分布式數(shù)據(jù)庫引擎,吸收了Cobar核心的Cobar-Proxy源碼,實現(xiàn)了一套獨立的類似MySQL-Proxy協(xié)議的解析端,能夠?qū)魅氲腟QL進行解析和處理,對應用程序屏蔽各種復雜的底層DB拓撲結構,獲得單機數(shù)據(jù)庫一樣的使用體驗,同時借鑒了淘寶TDDL豐富的分布式數(shù)據(jù)庫實踐經(jīng)驗,實現(xiàn)了對分布式Join支持,SUM、MAX、COUNT、AVG等聚合函數(shù)支持以及排序等函數(shù)支持,通過異構索引、小表廣播等解決分布式數(shù)據(jù)庫使用場景下衍生出的一系列問題,最終形成了完整的分布式數(shù)據(jù)庫方案。
使用場景
分布式數(shù)據(jù)庫核心訴求在于解決單機數(shù)據(jù)庫的瓶頸,單機數(shù)據(jù)庫在使用過程中不可避免會遇到數(shù)據(jù)庫容量、連接數(shù)、事務數(shù)、讀性能瓶頸,突破這些瓶頸的兩種通用的解決模型是單機垂直擴展scale up模型和水平擴展scale out模型。
單機擴展模型和硬件資源強綁定,普遍采用升級單機硬件能力的方式,實現(xiàn)數(shù)據(jù)庫服務能力擴展,比如原來采用MySQL單機數(shù)據(jù)庫,遇到訪問瓶頸時更換磁盤,訪問量更高時就需要考慮使用Oracle的商用解決方案、高端的存儲設備、高端小型機,也就是IOE架構,甚至升級IOE設備,以換取更高的擴展和服務能力,這個過程就會存在設備升級和數(shù)據(jù)遷移的成本。
多機器用水平擴展模型使用大量廉價的PC-Server,通過陣列的方式來實現(xiàn)數(shù)據(jù)庫的水平擴容,優(yōu)勢在于成本更低,因為不需要淘汰老設備和系統(tǒng),不需要頻繁遷移數(shù)據(jù),需要時,只需擴容服務集群規(guī)模。
使用分布式多機模型也需要付出一定成本,分布式數(shù)據(jù)庫的架構與單機數(shù)據(jù)庫的邏輯和物理分布存在比較大差異,因此需要將單機數(shù)據(jù)庫的數(shù)據(jù)遷移到分布式架構模型之下,也就是Sharding的數(shù)據(jù)分片過程,這個過程涉及數(shù)據(jù)的分布式邏輯設計、數(shù)據(jù)庫遷移和SQL的優(yōu)化改造,當然這個遷移一次性的,當架構遷移完成之后,就無需再關心數(shù)據(jù)庫擴容和數(shù)據(jù)遷移問題,因為分布式數(shù)據(jù)庫的服務層已經(jīng)集成了擴容功能,架構上支持水平能力擴展。
2006年之前我們的核心應用普遍采用Oracle數(shù)據(jù)庫,但隨著業(yè)務快速發(fā)展,淘寶的數(shù)據(jù)量和訪問量急劇增加,數(shù)據(jù)庫出現(xiàn)嚴重訪問性能問題,導致數(shù)據(jù)庫頻繁宕機、業(yè)務停滯,即使當時已經(jīng)使用Oracle亞洲最大的RAC集群,單機數(shù)據(jù)庫的擴展能力已經(jīng)達到極限,且需要付出巨大的資金和運維成本,因此我們基于自己的實際情況,逐步開始去IOE,研發(fā)分布式關系型數(shù)據(jù)庫服務,實現(xiàn)數(shù)據(jù)庫的高擴展和成本可控,目前DRDS已經(jīng)成為我們內(nèi)部分布式數(shù)據(jù)庫的標準,并且對外服務于金融、制造、政府機構、電商、社交等各行業(yè)。
DRDS的整體架構
DRDS/TDDL是典型的水平擴展分布式數(shù)據(jù)庫模型,區(qū)別于傳統(tǒng)單機數(shù)據(jù)庫share anything架構,DRDS/TDDL采用share nothing架構,share nothing架構核心思路利用普通的服務器,將單機數(shù)據(jù)拆分到底層的多個數(shù)據(jù)庫實例上,通過統(tǒng)一的Proxy集群進行SQL解析優(yōu)化、路由和結果聚合,對外暴露簡單唯一的數(shù)據(jù)庫鏈接。整體架構如圖1所示,包含DRDS服務模塊、DRDS管控模塊、配置中心、監(jiān)控運維、數(shù)據(jù)庫服務集群、域名服務模塊。
通過分布式集群管理模塊實現(xiàn)對集群節(jié)點的管控。在數(shù)據(jù)安全和服務可用性方面,通過高效的數(shù)據(jù)同步系統(tǒng),實現(xiàn)數(shù)據(jù)庫的擴容和數(shù)據(jù)庫實例的主備數(shù)據(jù)同步。同時依賴實例監(jiān)控模塊和HA模塊實現(xiàn)主備的監(jiān)控和自動化容災切換。作為成熟的分布式數(shù)據(jù)庫產(chǎn)品,TDDL也具備完善的運維管控系統(tǒng),能夠?qū)崿F(xiàn)分布式數(shù)據(jù)庫多實例之間的配置管理、變更,以及各種數(shù)據(jù)同步、擴容等任務管理,降低運維成本。
DRDS/TDDL的功能特性
數(shù)據(jù)分片
DRDS的基礎原理就是Sharding,也就是數(shù)據(jù)分片。將單機數(shù)據(jù)庫的數(shù)據(jù)拆分到多個單機數(shù)據(jù)庫上,對外保持邏輯的一致性。后端拆分的數(shù)據(jù)庫為分庫,對應的表稱為分表,每個分庫負責一份數(shù)據(jù)的讀寫操作,分散整體訪問壓力。在系統(tǒng)擴容時,只需水平增加分庫數(shù)量,并遷移相關數(shù)據(jù),即可提高DRDS系統(tǒng)總體容量。
數(shù)據(jù)分片需要選擇一個分片的拆分緯度,也就是數(shù)據(jù)分布的依據(jù)。比如一個用戶訂單信息表,如果按照訂單ID做數(shù)據(jù)拆分,那么相同訂單ID的數(shù)據(jù)就會被拆分到同一個數(shù)據(jù)庫存儲節(jié)點,如果按照用戶ID做數(shù)據(jù)拆分,那么同一個用戶的訂單就會分布到同一個數(shù)據(jù)庫存儲實例的存儲節(jié)點。
拆分緯度的選擇非常重要,一般來說要根據(jù)實際業(yè)務的場景選擇拆分鍵,總體指導原則是盡量保證每一個數(shù)據(jù)庫節(jié)點的數(shù)據(jù)量和負載更均衡,單條SQL操作盡量落到單個數(shù)據(jù)庫節(jié)點執(zhí)行,不同SQL的查詢落到不同的數(shù)據(jù)庫節(jié)點。這樣可以減少多個節(jié)點之間的網(wǎng)絡傳輸,保持分布式查詢的效率,均衡負載的同時也便于擴展。
平滑擴容
數(shù)據(jù)庫的擴容是數(shù)據(jù)庫運維的常見操作,當數(shù)據(jù)庫的數(shù)據(jù)存儲容量不足時,傳統(tǒng)的單機數(shù)據(jù)庫需要提升單機的存儲空間來支持更大的數(shù)據(jù)寫入量,而隨著數(shù)據(jù)量膨脹,同樣的SQL查詢語句,查詢的基礎數(shù)據(jù)量增加必然會降低查詢效率;同時隨著數(shù)據(jù)量增加,數(shù)據(jù)庫的訪問壓力通常也會成倍提升,造成單機數(shù)據(jù)庫連接數(shù)到達極限,此時單機數(shù)據(jù)庫就需要通過升級硬件規(guī)格,使用磁盤陣列,使用高端的存儲介質(zhì)設備和更高端的小型機服務器來承載數(shù)據(jù)量和訪問量的增加,這個過程會伴隨大量的數(shù)據(jù)遷移,為了保證數(shù)據(jù)的一致性通常需要停機數(shù)據(jù)遷移,對業(yè)務影響較大。
DRDS的分布式架構采用平滑擴容的方式來解決上述問題,通過增加更多的底層數(shù)據(jù)庫實例來完成整體集群擴容。
平滑擴容的前提是用戶需要按照前述的分庫分表邏輯,將邏輯數(shù)據(jù)庫拆分為多個物理分庫,不同的分庫落在不同的底層物理數(shù)據(jù)庫機器上。分庫分表的數(shù)量通常建議用戶預估未來3-5年的數(shù)據(jù)量增長情況,按照這個數(shù)據(jù)量計算總體數(shù)據(jù)應該拆分為多少個分庫,因為單個分庫的數(shù)據(jù)量通常會有一個建議值,超過這個閾值就會造成單個節(jié)點性能下降。有了具體的分庫數(shù)量后,就可以按照分庫的邏輯將數(shù)據(jù)拆分到不同的存儲實例節(jié)點上,當承載分庫的物理數(shù)據(jù)庫機器出現(xiàn)容量和連接數(shù)不足等瓶頸問題時,就可以新增物理數(shù)據(jù)庫節(jié)點,將原有的分庫遷移到新的物理數(shù)據(jù)庫節(jié)點上,實現(xiàn)整體邏輯數(shù)據(jù)庫的擴容。
擴容過程實際是物理數(shù)據(jù)遷移的過程,引擎層按照分庫遷移后的邏輯先在物理節(jié)點上建立新的分庫,然后保留一個時間點進行全量的數(shù)據(jù)遷移。完成全量遷移后,開始基于先前保留的時間點進行增量的數(shù)據(jù)追趕。當增量數(shù)據(jù)追趕到兩邊的數(shù)據(jù)幾乎一致時,對數(shù)據(jù)庫進行瞬時停寫,將最后的數(shù)據(jù)追平,引擎層進行分庫邏輯的路由切換,路由規(guī)則切換完成后就完成了核心的擴容邏輯,整個切換過程在毫秒級別完成。
為了保證數(shù)據(jù)本身的安全,便于擴容回滾,在路由規(guī)格切換完成后,遷移前后的邏輯分庫數(shù)據(jù)還會進行實時同步,直到業(yè)務確認后,才可清理原有分庫數(shù)據(jù)。
整個擴容過程對上層的業(yè)務訪問幾乎無感知,是完全平滑的擴容,但仍需注意擴容的操作盡量選擇在數(shù)據(jù)庫訪問,尤其是寫入的低谷期進行,避免切換時過多的數(shù)據(jù)追趕時間。
分布式MySQL執(zhí)行引擎
分布式數(shù)據(jù)庫的數(shù)據(jù)有規(guī)律地存儲在多個底層存儲實例上,數(shù)據(jù)物理存儲的變化會造成與原生的數(shù)據(jù)庫引擎不兼容,單機數(shù)據(jù)庫所有的數(shù)據(jù)讀取、寫入、計算都在單一的物理機上執(zhí)行,數(shù)據(jù)狀態(tài)維持在單機上,主要的性能消耗在于磁盤的數(shù)據(jù)讀取;而分布式架構下,數(shù)據(jù)和狀態(tài)需要在多個數(shù)據(jù)庫實例之間以及底層實例和Proxy之間進行傳輸,這會造成網(wǎng)絡I/O消耗,而網(wǎng)絡I/O對性能造成消耗相較于本地磁盤I/O和本地計算的性能開銷而言要大得多。
因此分布式SQL引擎主要目標是實現(xiàn)與單機數(shù)據(jù)庫SQL引擎的完全兼容,實現(xiàn)SQL的智能下推。能夠智能分析SQL,解析出哪些SQL可以直接下發(fā),哪些SQL需要進行優(yōu)化改造,優(yōu)化成什么樣,以及路由到哪些實例節(jié)點上執(zhí)行,充分發(fā)揮數(shù)據(jù)庫實例的全部能力,減少網(wǎng)絡之間的數(shù)據(jù)傳輸量,最終對不同實例處理后的少量結果數(shù)據(jù)進行聚合計算返回給應用調(diào)用方。這就是分布式SQL引擎的智能下推功能。
分布式引擎的職責包含SQL解析、優(yōu)化、執(zhí)行和合并四個流程,如圖4所示。
智能下核心原則有如下幾個:
- 減少網(wǎng)絡傳輸;
- 減少計算量,盡量將計算下推到下層的數(shù)據(jù)節(jié)點上,讓計算在數(shù)據(jù)所在的機器上執(zhí)行;
- 充分發(fā)揮下層存儲的全部能力。
基于以上原則實現(xiàn)的SQL引擎,就可以做到服務能力線性擴展。比如一個簡單的AVG操作,對于一些比較初級的分布式數(shù)據(jù)庫模型而言,常見做法是把AVG直接下發(fā)到所有的存儲節(jié)點,這樣造成的結果就是語法兼容,語意不兼容,最終拿到的是錯誤結果。而DRDS的智能下推引擎,對SQL的語法做充分的語意兼容性適配,針對AVG操作,只能由引擎將邏輯AVG SQL解析優(yōu)化為SUM和COUNT的SQL然后進行下推,由底層的數(shù)據(jù)庫實例節(jié)點完成SUM和COUNT計算,充分利用底層節(jié)點的計算能力,在引擎層將各個存儲節(jié)點的SUM和COUNT結果聚合計算,最終計算出AVG。這只是一個非常典型的案例,在分布式數(shù)據(jù)庫模型下,多數(shù)據(jù)表的Join操作,歸并排序的兼容性非常復雜,下文會針對典型的場景解析TDDL/DRDS如何解決分布式場景下的具體問題。
彈性擴展
TDDL/DRDS采用服務和存儲分離的架構,DRDS實例服務層通過集群方式部署,由多個服務節(jié)點構成一個服務實例,通過負載均衡以及域名服務對外提供服務,多個服務節(jié)點之間無狀態(tài)同步,平均負載處理用戶請求。服務集群處理能力不足時,可隨時擴充服務節(jié)點,增加服務處理能力。同樣,業(yè)務低谷期也可適當降低集群規(guī)模,做到彈性的服務能力擴展。
對于一些大數(shù)據(jù)量OLAP的場景,對于單個Server節(jié)點的內(nèi)存資源需求高時,也可通過提升單個Server節(jié)點的規(guī)格,做到垂直的能力擴展。
分布式Join和小表廣播
分布式場景下的Join操作和單機不同,單機數(shù)據(jù)的Jion操作發(fā)生在單機上,不存在內(nèi)部網(wǎng)絡數(shù)據(jù)傳輸。
在分布式架構下的多個數(shù)據(jù)表Jion,如果參與Join的多表數(shù)據(jù)切分緯度不同,數(shù)據(jù)則按照不同的拆分緯度分散在不同的數(shù)據(jù)庫實例上,Join操作可能產(chǎn)生跨多個物理分庫的Join,就需要進行多個底層實例的大量數(shù)據(jù)傳輸,SQL的執(zhí)行效率就得不到保證,因此要參與Join操作的數(shù)據(jù)表要盡量保持拆分緯度統(tǒng)一,讓Join操作盡量發(fā)生在單機上,減少跨庫Join。如果不能保持拆分緯度的統(tǒng)一,存在跨庫Join操作,那么原則就是盡量減少Join操作的輸出傳輸。
DRDS通常使用的Join算法基于Nested Loop,對于Join的左右兩個表,首先從Join的左表(驅(qū)動表)取出數(shù)據(jù),然后將所取出數(shù)據(jù)中Join列的值放到右表并進行IN查詢,從而完成Join過程。因此,Join的左表數(shù)據(jù)量越少,DRDS對右表做IN查詢就次數(shù)就越少,如果右表的數(shù)據(jù)量也很少或建有索引,則Join的速度更快。故而在DRDS中,Join驅(qū)動表的選擇對于Join的優(yōu)化非常重要。
而在實際數(shù)據(jù)庫場景中,經(jīng)常有一些源信息表,數(shù)據(jù)量較小,更新頻度也很低,這些表無需拆分,類似這些源信息表通常采用單表模式,單表模式下一個邏輯表的數(shù)據(jù)統(tǒng)一存儲在一個分庫中,通常存儲在“0”庫,將這些表定義為“小表”,而其他業(yè)務數(shù)據(jù)量大、更新頻率高的表仍舊采用分庫分表的拆分模式。那這些“小表”和分庫分表進行Join時,基于Nested Loop算法的原則,小表作為Join的驅(qū)動表會大大減少右表的IN查詢次數(shù),同時DRDS提供的小表廣播功能,通過數(shù)據(jù)實時復制,將“小表”的全量數(shù)據(jù)和增量變更實時復制到分庫分表上,將跨庫的Join轉(zhuǎn)化為單機Join操作,減少Server節(jié)點的計算,降低數(shù)據(jù)在多個底層實例之間的傳輸,Jion的效率提升會非常明顯。
異構索引
異構索引是DRDS提升分布式查詢效率的解決方案之一,能夠解決分布式場景下數(shù)據(jù)拆分緯度和數(shù)據(jù)查詢使用緯度不一致導致的低效問題。
當數(shù)據(jù)表被拆分為多個分庫分表時,數(shù)據(jù)在分庫分表的分布規(guī)則就固定了。但是通常數(shù)據(jù)的業(yè)務使用場景非常復雜,如果數(shù)據(jù)的查詢緯度和數(shù)據(jù)拆分分布的規(guī)則一致,單條SQL會在一個分庫分表上執(zhí)行;如果數(shù)據(jù)的查詢使用緯度和數(shù)據(jù)拆分分布的規(guī)格不一致,單條SQL就很有可能在多個分庫分表上執(zhí)行,出現(xiàn)跨庫查詢,跨庫查詢會增加網(wǎng)絡I/O的成本,查詢效率必然下降。
解決這個問題的思路還是分布式數(shù)據(jù)庫的一貫原則,讓SQL執(zhí)行在單庫上完成,實際采用的方式就是用“空間換效率”的方案,也就是將同一份數(shù)據(jù)表,冗余存儲多份,按照不同的業(yè)務使用場景進行拆分,保持拆分緯度和使用緯度統(tǒng)一,而多份數(shù)據(jù)之間會實時數(shù)據(jù)復制以解決數(shù)據(jù)一致性問題,這就是“異構索引”方案。當然異構索引表不能無限制濫用,過多的異構索引表會影響同步效率,對源數(shù)據(jù)表造成同步壓力。
最佳實踐
分布式SQL優(yōu)化
SQL優(yōu)化是數(shù)據(jù)庫使用和運維的日常操作,分布式數(shù)據(jù)庫針對SQL的優(yōu)化不僅要考慮磁盤I/O的開銷,更要關注網(wǎng)絡I/O開銷。為了優(yōu)化SQL執(zhí)行,其核心的優(yōu)化思想就是減少網(wǎng)絡I/O。為此,DRDS會盡量將原本DRDS這一層的工作均衡下發(fā)到其底層的各個分庫(如RDS 等)來做。這樣就可以將原本需要走網(wǎng)絡的I/O開銷轉(zhuǎn)換為單機的磁盤I/O開銷,從而提升查詢執(zhí)行效率。因此,我們在使用DRDS時若遇到了慢SQL,則需針對DRDS的特點將適當改寫SQL。
首先是條件查詢優(yōu)化,DRDS的數(shù)據(jù)按拆分鍵進行水平切分,查詢中若帶上拆分鍵對于減少SQL在DRDS的執(zhí)行時間很有意義。查詢條件盡量帶分庫鍵,就可以讓DRDS根據(jù)分庫鍵的值將查詢直接路由到特定的分庫,這有助于避免DRDS做全庫掃描。含分庫鍵的條件精度越高,越有助于提高查詢速度,也只有這樣的優(yōu)化才能充分發(fā)揮分布式架構查詢的優(yōu)勢,便于后續(xù)查詢能力的擴展。
其次針對Join的優(yōu)化,選擇條件查詢數(shù)據(jù)量少的Join表作為左表(驅(qū)動表),降低右表IN查詢的次數(shù);在數(shù)據(jù)量少且變更量少的“廣播表”參與的Jion操作,將“廣播表”作為驅(qū)動表。
針對LIMIT OFFSET、COUNT語句,DRDS實際SQL執(zhí)行是依次將OFFSET之前的記錄數(shù)據(jù)讀取出來,并丟棄,只保留OFFSET之后的數(shù)據(jù),這樣當OFFSET非常大時,讀取的數(shù)據(jù)記錄數(shù)很少,效率也很低,因為OFFSET之前的數(shù)據(jù)讀取需要執(zhí)行大量的磁盤I/O讀取操作。優(yōu)化方式是將SQL優(yōu)化為對key的OFFSET讀取和IN操作兩個步驟,先讀取OFFSET之后的記錄key,內(nèi)存中緩存這些key,然后再通過IN查詢獲取完整的記錄信息,這樣會大大減少磁盤I/O,效率提升非常明顯。
分布式事務優(yōu)化
分布式數(shù)據(jù)庫的事務和SQL查詢優(yōu)化的邏輯是一樣的原則,盡量讓事務在單庫中執(zhí)行,只有在單庫中執(zhí)行,才可以在保持事務ACID特性的同時,還能線性地擴展事務能力。這種單庫事務通常稱為“強事務”。
實際業(yè)務也經(jīng)常會面臨分布式數(shù)據(jù)庫架構下,數(shù)據(jù)庫事務不可避免出現(xiàn)跨庫執(zhí)行??鐜焓聞毡厝簧婕暗揭粋€事務在多個分庫上進行事務分支的執(zhí)行和狀態(tài)同步,相比單機事務,分布式跨庫事務的吞吐量和延遲會大大增加。而事務涉及的分庫越多,事務邊界越大,事務的延遲也會相應增加,性能就會出現(xiàn)線性衰減。
遇到跨庫事務,通常的實踐優(yōu)化方式是通過“最終一致”事務保證事務執(zhí)行的吞吐量。“最終一致”事務的原理是優(yōu)先保證核心事務分支的正向執(zhí)行,然后保存事務中間狀態(tài),其他事務分支異步執(zhí)行,執(zhí)行完成后達到最終的事務一致,避免跨庫事務時間序列執(zhí)行阻塞,提升事務吞吐量。如圖8所示,事務3和事務5是跨庫事務,事務分支先在左邊庫進行,異步的事務分支在右邊分庫執(zhí)行,分別在自己所在的分庫順序執(zhí)行,最終達到事務一致性。
單機數(shù)據(jù)庫遷移到DRDS的流程
單機數(shù)據(jù)庫遷移到分布式數(shù)據(jù)庫要保證的就是業(yè)務正常運轉(zhuǎn)、平滑過渡、減少運維,整個遷移分為三個步驟。
- 第一步,讀寫保持在原有的數(shù)據(jù)庫上,數(shù)據(jù)通過復制機制寫入分布式數(shù)據(jù)庫,前提是分布式目標庫表已經(jīng)建好;
- 第二步,驗證云上數(shù)據(jù)是否正確,切部分讀取流量到目標的分布式數(shù)據(jù)庫上讀取線上壓力驗證(測試環(huán)境提前驗證也可保證);
- 第三步,業(yè)務聽寫幾分鐘,讀寫的流量切換到目標庫,數(shù)據(jù)反向復制到源單機數(shù)據(jù)庫,保證隨時可切換回單機數(shù)據(jù)庫,同時也可做云下數(shù)據(jù)備份。
未來的發(fā)展
DRDS作為分布式數(shù)據(jù)體系中的數(shù)據(jù)庫服務中間層,未來會適配更多底層存儲引擎,在充分利用底存儲節(jié)點的計算能力的同時,優(yōu)化本身服務的計算能力,解決OLAP場景,成為能夠完整覆蓋OLTP和OLAP以及其他一些數(shù)據(jù)庫服務場景的完備的分布式數(shù)據(jù)庫服務體系。同時完備分布式數(shù)據(jù)庫邏輯層的運維支持和分布式強一致事務的支持。