沒有完美的分布式架構
這是“沒有完美”系列的第三篇,以前我寫過《沒有完美的數據庫》,《沒有完美的存儲引擎》,今天我們來聊聊目前最為熱門的分布數據庫架構。大家要注意今天我們討論的不是分布式數據庫本身,而是分布式數據庫架構。分布式數據庫太復雜了,也太多了,要想簡單分析一下夠一篇萬字長文了,今天我們重點來分析一些目前最常見的國產分布式數據庫架構,即使縮小到這個話題,實際上也是十分復雜的,我們今天只做一些粗略的分析。如果有朋友覺得這篇文章還算有點價值,并且對其中某些點希望進一步分析,請留言通知,我找時間寫一些。
這篇文章是今天早上在上班路上開始思考的,7點20進入辦公室開始寫,8點40寫完,只用了一個多小時,因此難免一些觀點有些考慮不周,請讀者見諒了。
現今國產分布式數據庫市場十分熱鬧,僅僅是交易型關系型數據庫就有近百款,大家在選擇數據庫的時候也容易挑花了眼。以前我也寫文章討論過,沒有完美的數據庫,用戶應該根據其場景和應用需求來選擇合適自己的數據庫產品。數據庫架構也是其中的一個重要的選擇要素,特別是分布式數據庫,不同的架構其優缺點十分明顯,某些分布式數據庫存在的缺陷因為架構問題,是較難解決的,因此了解某個分布式數據庫屬于哪種架構,有哪些優點和缺點十分重要。
分布式數據計算最主要有兩種形式,一種是分區SHARDING,一種是全局一致性HASH,根據這種分布式數據計算的種類不同,衍生出存算分離和存算一體這兩種分布式數據庫架構。進一步再分出一系列子類別,比如對等模式,代理模式,外掛模式等。
存算一體SHARDING模式的分布式數據庫最為典型的是Oceanbase。下面是OB的架構圖:

每個Observer是一個計算存儲一體化的獨立服務,帶有SQL引擎,事務引擎和存儲引擎,并存儲一部分分片數據(分區)。OB采用的是對等模式,客戶端連接到任何一個OBSERVER都可以全功能使用數據庫。OBSERVER會自動產生分布式執行計劃,并將算子分發到其他參與協同計算的OBSERVER上,完成分布式計算。管理整個集群的RootService只在部分Observer中存在,并全局只有一個是主的,其他都是備的。實際上每個OBSERVER就組成了一個完整的數據庫,如果所有的數據都存儲于一個OBSERVER上,我們的訪問方式類似于一個集中式數據庫。
這種架構下的數據分區采用了高可用,一個數據寫入主副本(Leader)后會自動同步到N個備副本,OB采用Paxos分區選舉算法來實現高可用。其備副本可以用于只讀或者弱一致性讀(當副本數據存在延遲時),從而更為充分的利用多副本的資源。實際上不同的分布數據庫的主備副本的使用方式存在較大的差異,有些分布式數據庫的備副本平時是不能提供讀寫的,只能用于高可用。
大家看到這里,可能覺得OB的架構不錯,各種問題都考慮得比較周到了。不過還是那句話,沒有完美的分布式數據庫架構,SHARDING存儲需要分布在各個SHARDING分區中的分區表要有一個SHARDING KEY,根據SHARDING KEY來做分片處理。SHARDING模式雖然應對一般的數據庫操作是沒問題了,不過如果一條復雜的查詢語句中沒有關于SHARDING KEY,那么就沒辦法根據SHARDING KEY去做分區裁剪,必須把這個算子分發到集群中存儲這張表的分區的所有OBSERVER上,這種情況被稱為SHARDING數據庫的讀放大。
采用SHARDING模式的數據庫系統,你要區分其技術水平,重點要看針對讀放大的措施是否完善,你的應用系統能否采用這些方案來讓讀放大的影響變得更小。實際上OB在這方面做了不少工作,今天篇幅問題,我們不展開討論了。
既然存算一體的SHARDING模式有這種缺陷,那么能不能采用存算分離的方案呢?大家來看TIDB的架構:

TIDB是采用存算分離的,計算引擎TIDB和存儲引擎TIKV是分離的,因此TIDB不存在存算一體的SHARDING分布式數據庫的這種讀放大的問題。似乎TIDB要技高一籌,完美地解決了OB等SHARDING數據庫的問題。不過事實上沒有那么簡單,就像我上大學時的老師陳道蓄先生說過的“任何好東西都是有代價的”,存算分離解決了SHARDING的讀放大問題,但是又引入了一個新的問題。那就是計算節點的本地緩沖問題。因為大型分布式計算環境下實施緩沖區融合成本極高,因此每個TIDB節點只能有本地緩沖,不能有全局緩沖。因此在TIDB的SQL引擎上是沒有全局DB CACHE的,TIDB的數據緩沖只能建立在TIKV和TIFLASH上。SQL引擎->DB CACHE->存儲路徑->數據存儲介質這種傳統數據庫的數據讀取模式變成了SQL引擎->本地局部緩沖->存儲路徑(網絡)->存儲節點緩沖->存儲介質這種模式。其效率大大地降低了。為了解決這個問題,采用此類架構的數據庫系統,就必須采用更快的存儲介質,從而解決缺乏全局DB CACHE支持的問題。當然這個問題也并不是不能解決的,通過更為細致的算子下推,采用類似ORACLE的SMART SCAN等技術,可以解決大部分問題。
實際上,國產分布式數據庫使用的最多的架構并不是上面的這兩種,而是基于SQL代理的存算分離架構,這種架構采用的數據存儲方式也是SHARDING方式,不過使用了一個SQL PROXY。騰訊TDSQL、熱璞HOTDB、中興通訊GOLDENDB、百度GAIADB、京東STARDB、openGauss分布式計算框架等著名分布式數據庫都是采用類似的架構。

TDSQL由調度器、網關、Agent三個核心組件加上MySQL組成,其中調度器是調度分布式計算的,內部又分為zookeeper和scheduler server;接入網關是一個SQL代理,應用連接到接入網關上進行SQL操作。代理與MySQL實例一起,構成數據節點。多個數據節點構成服務單元SET。每個SET是一組高可用組,由一主多備組成。數據按照SET進行SHARDING分區。
這種架構中的存儲引擎是一個完整的數據庫,大部分采用此類架構的分布式數據庫采用了MYSQL作為存儲引擎的核心,這是因為MYSQL管理起來比較輕量級,出問題大部分情況下隔離故障或者重啟實例就可以解決。這種架構在網關(代理)層就具有分布式計算的能力,在此層分解SQL,產生分布式執行計劃,然后將分解出的SQL分配給參與本次計算的所有存儲節點,存儲節點執行SQL,獲得所需要的數據集,傳輸給網關,由網關進行聚合計算。
這種架構的底層包含一個完整的數據庫引擎,因此可以把研發的主要精力放在網關SQL引擎上,大大降低了開發難度。不過雖然很多數據庫產品都是采用了此架構,各個廠商的技術能力不同,因此做出來的產品在性能,SQL支持能力等方面都存在較大的差距。存儲層采用完整的數據庫引擎也是個雙刃劍,如果不對數據庫引擎的優化器做大的優化,則數據庫引擎在性能上的弱點會帶壞整個分布式數據庫的性能,而去做優化又是大投入的工作。也是無法兩全其美的。
另外高可用方面的主從選擇,切主算法的實現水平方面,也會影響此架構的數據庫的用戶使用體驗。最初此類架構的主從切換類似于MYSQL的主從切換,對前端應用的影響很大,如果業務高峰時出現主DN故障,那就是災難性的。現在一些頭部企業在這方面做了極大的優化,用戶體驗也得到了很大的改善。
還有一些分布式數據庫是采用SQL ON HADOOP架構的,比如易鯨捷。

其存儲引擎是一個經過了優化的HBASE兼容的引擎,在此基礎上構建整個分布式數據庫系統。此種架構的優勢是底層采用了一個十分成熟的分布式存儲系統HDFS和分布式數據庫HBASE,天生對于大數據量的計算以及物聯網場景的應用有較好的支持。不過因為HADOOP本身并不是為低延時高并發的OLTP場景甚至HTAP場景設計的,因此存儲引擎很可能會成為今后數據庫的瓶頸。2017年我在貴州遇到時任易鯨捷cto的丁洪先生的時候,他也認為易鯨捷要脫胎換骨,必須重寫存儲引擎,使之更適合HTAP的應用場景。
時間關系,今天就聊到這里了,似乎還是沒聊透。實際上就本文的標題“沒有完美的分布式數據庫架構”,我們的分布式數據庫架構好壞的爭論不妨放一放,大家都多一點考慮自身架構的缺點,想辦法去彌補自身的缺陷,那才是對用戶負責。
解決分布式計算的問題,在分布式SQL引擎優化,算子下推,避免讀放大,主副本切換、讀寫分離、運維可觀測性提升等方面,都是需要認真去不斷提升的。選擇分布式數據庫,僅僅看架構是不夠的,更重要的是看數據庫廠商是不是針對我們的應用場景做了很好的優化,產品能否滿足我們應用場景的需求。