阿里雲、Amazon、Google雲資料庫方案架構與技術分析

天府雲創發表於2016-12-12

「一切都會執行在雲端」。

現在越來越多的業務從自己維護基礎設施轉移到公有(或者私有)雲上, 帶來的好處也是無需贅述的,極大降低了 IaaS 層的運維成本,對於資料庫層面來說的,以往需要很強的 DBA 背景才能搞定彈性擴容高可用什麼的高階動作,現在大多數雲服務基本都或多或少提供了類似的服務。

Amazon RDS

其實說到公有云上的雲資料庫,應該最早 Amazon 的 RDS,最早應該是在 2009 年釋出的,Amazon RDS 的架構類似在底層的資料庫上構建了一箇中間層(從架構上來看,阿里雲 RDS,UCloud RDS 等其他雲的 RDS 服務基本是大同小異,比拼的是功能多樣性和實現的細節)。

這個中間層負責路由客戶端的 SQL 請求發往實際的資料庫儲存節點,因為將業務端的請求通過中間層代理,所以可以對底層的資料庫例項進行很多運維工作,比如備份,遷移到磁碟更大或者 IO 更空閒的物理機等。這些工作因為隱藏在中間層後邊,業務層可以做到基本沒有感知,另外這個中間路由層基本只是簡單的轉發請求,所以底層可以連線各種型別的資料庫。

所以一般來說,RDS 基本都會支援 MySQL / SQLServer / MariaDB / PostgreSQL 等流行的資料庫,對相容性基本沒有損失,而且在這個 Proxy 層設計良好的情況下,對效能的損失是比較小的。另外有一層中間層隔離底層的資源池,對於資源的利用和排程上可以做不少事情。

簡單舉個例子,比如有一些不那麼活躍的 RDS 例項可以排程在一起共用物理機,比如需要線上擴容只需要將副本建立在更大磁碟的機器上,在 Proxy 層將請求重新定向即可,比如定期的資料備份可以放到 S3 上,這些一切都對使用者可以做到透明。

但是這樣的架構缺點也同樣明顯:本質上還是一個單機主從的架構,對於超過最大配置物理機的容量,CPU 負載,IO 的場景就束手無策了。隨著很多業務的資料量併發量的增長,尤其是移動網際網路的發展,無限的可擴充套件性成為了一個很重要需求。當然對於絕大多數資料量要求沒那麼大,單例項沒有高併發訪問的庫來說,RDS 仍然是很適合的。

Amazon DynamoDB

對於剛才提到的水平擴充套件問題,一些使用者實在痛的不行,甚至能接受放棄掉關係模型和 SQL。比如一些網際網路應用業務模型比較簡單,但是併發量和資料量巨大,應對這種情況,Amazon 開發了 DynamoDB,並於 2012 年初發布 DynamoDB 的雲服務。其實 Dynamo 的論文早在2007年就在 SOSP 發表,這篇有歷史意義的論文直接引爆了 NoSQL 運動,讓大家覺得原來資料庫還能這麼搞。

Dynamo 對外主打的特點是水平擴充套件能力和通過多副本實現(3副本)的高可用,另外在 API 的設計上可以支援最終一致性讀取和強一致性讀取,最終一致性讀取能提升讀的吞吐量。

但是請注意,DynamoDB 雖然有強一致讀,但是這裡的強一致性並不是傳統我們在資料庫裡說的 ACID 的 C,而且由於沒有時序的概念(只有 vector clock),對於衝突的處理只能交給客戶端,Dynamo 並不支援事務。不過對於一些特定的業務場景來說,擴充套件能力和可用性是最重要的,不僅僅是容量,還有叢集的吞吐。

阿里雲 DRDS

但是那些 RDS 使用者的資料量也是在持續增長的,對於雲服務提供商來說不能眼睜睜的看著這些 RDS 使用者資料量一大就走掉或者自己維護資料庫叢集。因為也不是誰都能徹底重構程式碼到 NoSQL 之上,並且分庫分表其實對於業務開發者來說是一個很痛苦的事情,在痛苦中往往是蘊含著商業機會的。

比如對於 RDS 的擴充套件方案,我介紹兩個比較典型的:第一個是阿里雲的 DRDS (不過現在好像從阿里雲的產品列表裡拿掉了?),DRDS 其實思路很簡單,就是比 RDS 多一小步,在剛才提到的 RDS 的中間層中加入使用者配置的路由策略,比如使用者可以指定某個表的某些列作為 sharding key 根據一定規則路由到特定的例項,也可以垂直的配置分庫的策略。

其實 DRDS 的前身就是淘寶的 TDDL,只不過原來 TDDL 是做在 JDBC 層,現在將 TDDL 做進了 Proxy 層(有點像把 TDDL 塞到 Cobar 的感覺)。這樣的好處是,將應用層分庫分表的工作封裝起來了,但是本質上仍然是一箇中介軟體的方案,儘管能對簡單的業務做到一定程度的 SQL 相容。

對於一些複雜查詢,多維度查詢,跨 Shard 事務支援都是有限了。畢竟中間路由層對 SQL 的理解有限,至於更換 Sharding key 、DDL、備份也是很麻煩的事情。從 Youtube 開源的中介軟體 Vitess 的實現和複雜程度來看甚至並不比實現一個資料庫簡單,但是相容性卻並沒有重新寫一個資料庫來得好。

Amazon Aurora

後來時間來到了2015年,Amazon 走了另外一條路。在2015年,Amazon Aurora 釋出。Aurora 的資料在公網上並不多,Aurora 提供了 5x 於單機 MySQL 5.6的讀吞吐能力,不過最大也就擴充套件到15個副本,副本越多對寫吞吐影響越大,因為只有一個 Primary Instance 能提供寫入服務,單個副本最大支援容量 64T,而且支援高可用以及彈性的擴充套件。

值得一提的是 Aurora 的相容性。其實做資料庫的都知道,相容性是一個很難解決的問題,可能實現上很小的差異就會讓使用者的遷移成本變得很大,這也是為什麼中介軟體和分庫分表的方案如此反人類的原因,我們大多都在追求使用者平滑的遷移體驗。

Aurora 另闢蹊徑,由於公開的資料不多,我猜想 Aurora 在 MySQL 前端之下實現了一個基於 InnoDB 的分散式共享儲存層( https://www.percona.com/blog/2015/11/16/amazon-aurora-looking-deeper/ )。對於讀例項來說是很好水平擴充套件的,這樣就將 workload 均攤在前端的各個 MySQL 例項上,有點類似 Oracle RAC 那樣的 Share everything 的架構。

這個架構的好處相對中介軟體的方案很明顯,相容性更強,因為還是複用了 MySQL 的 SQL 解析器。優化器,業務層即使有複雜查詢也沒關係,因為連線的就是 MySQL。

但是也正是由於這個原因,在節點更多,資料量更大的情況下,查詢並不能利用叢集的計算能力(對於很多複雜查詢來說,瓶頸出現在 CPU 上),而且 MySQL 的 SQL 優化器能力一直是 MySQL 的弱項,而且對於大資料量的查詢的 SQL 引擎的設計是和單機有天壤之別的。一個簡單的例子,分散式 Query Engine 比如 SparkSQL / Presto / Impala 的設計肯定和單機的 SQL 優化器完全不同,更像是一個分散式計算框架。

所以我認為 Aurora 是一個在資料量不太大的情況下(有容量上限),對簡單查詢的讀效能優化的方案,另外相容性比中介軟體的方案好得多。但是缺點是對於大資料量,複雜查詢的支援還是比較弱,另外對於寫入效能 Aurora 其實沒有做太多優化(單點寫入),如果寫入上出現瓶頸,仍然需要在業務層做水平或者豎直拆分。

Google Cloud BigTable

Google 作為大資料的祖宗一樣的存在,對於雲真是錯過了一波又一波:虛擬化錯過一波讓 VMWare 和 Docker 搶先了(Google 早在十年前就開始容器的方案,要知道容器賴以生存的 cgroups 的 patch 就是 Google 提交的);雲服務錯過一波讓 Amazon 搶先了(Google App Engine 真是可惜):大資料儲存錯過一波讓開源的 Hadoop 拿下了事實標準,以至於我覺得 Google Cloud BigTable 服務中相容 Hadoop HBase API 的決定,當時實現這些 Hadoop API for BigTable 的工程師心中應該是滴血的 :)

不過在被 Amazon / Docker / Hadoop 刺激到以後,Google 終於意識到社群和雲化的力量,開始對 Google Cloud 輸出 Google 內部各種牛逼的基礎設施,2015年終於在 Google Cloud Platform 上正式亮相。對於 BigTable 的架構相信大多數分散式儲存系統工程師都比較瞭解,畢竟 BigTable 的論文也是和 Amazon Dynamo 一樣是必讀的經典,我就不贅述了。

BigTable 雲服務的 API 和 HBase 相容,所以也是 {Key : 二維表格結構},由於在 Tablet Server 這個層次還是一個主從的結構,對一個 Tablet 的讀寫預設都只能通過 Tablet Master 進行,這樣使得 BigTable 是一個強一致的系統。這裡的強一致指的是對於單 Key 的寫入,如果服務端返回成功,接下來發生的讀取,都能是最新的值。

由於 BigTable 仍然不支援 ACID 事務,所以這裡的強一致只是對於單 Key 的操作而言的。對於水平擴充套件能力來說, BigTable 其實並沒有什麼限制,文件裡很囂張的號稱 Incredible scalability,但是 BigTable 並沒有提供跨資料中心(Zone)高可用和跨 Zone 訪問的能力。

也就是說,一個 BigTable 叢集只能部署在一個資料中心內部。這其實看得出 BigTable 在 Google 內部的定位,就是一個高效能低延遲的分散式儲存服務,如果需要做跨 Zone 高可用需要業務層自己做複製在兩個 Zone 之間同步,構建一個映象的 BigTable 叢集。

其實 Google 很多業務在 MegaStore 和 Spanner 出來之前,就是這麼搞的。對於 BigTable 來說,如果需要搞跨資料中心高可用,強一致,還要保證低延遲那是不太可能的,也不符合 BigTable 的定位。另外值得吐槽的是 BigTable 團隊發過一個 Blog :

( https://cloudplatform.googleblog.com/2015/05/introducing-Google-Cloud-Bigtable.html )。

裡面把 HBase 的延遲黑得夠嗆,一個 .99 的響應延遲 6 ms, HBase 280ms。其實看平均響應延遲的差距不會那麼大....BigTable 由於是 C++ 寫的,優勢就是延遲是相當平穩的。但是據我所知 HBase 社群也在做很多工作將 GC 帶來的影響降到最小,比如 off-heap 等優化做完以後,HBase 的延遲表現會好一些。

Google Cloud Datastore

在2011年,Google 發表了 Megastore 的論文,第一次描述了一個支援跨資料中心高可用 + 可以水平擴充套件 + 支援 ACID 事務語義的分散式儲存系統。 Google Megastore 構建在 BigTable 之上,不同資料中心之間通過 Paxos 同步,資料按照 Entity Group 來進行分片。Entity Group 本身跨資料中心使用 Paxos 複製,跨 Entity Group 的 ACID 事務需要走兩階段的提交,實現了 Timestamp-based 的 MVCC。

不過也正是因為 Timstamp 的分配需要走一遍 Paxos,另外不同 Entity Groups 之間的 2PC 通訊需要通過一個佇列來進行非同步的通訊,所以實際的 Megastore 的 2PC 的延遲是比較大的,論文也提到大多數的寫請求的平均響應延遲是 100~400ms 左右。據 Google 內部的朋友提到過,Megastore 用起來是挺慢的,秒級別的延遲也是常有的事情。

作為應該是 Google 內部第一個支援 ACID 事務和 SQL 的分散式資料庫,還是有大量的應用跑在 Megastore 上,主要是用 SQL 和事務寫程式確實能輕鬆得多。為什麼說那麼多 Megastore 的事情呢?因為 Google Cloud Datastore 的後端就是 Megastore。

其實 Cloud Datastore 在2011年就已經在 Google App Engine 中上線,也就是當年的 Data Engine 的 High Replication Datastore,現在改了個名字叫 Cloud Datastore,當時不知道背後原來就是大名鼎鼎的 Megastore 實在是失敬。

雖然功能看上去很牛,又是支援高可用,又支援 ACID,還支援 SQL(只不過是 Google 精簡版的 GQL)但是從 Megastore 的原理上來看延遲是非常大的,另外 Cloud Datastore 提供的介面是一套類似的 ORM 的 SDK,對業務仍然是有一定的侵入性。

Google Spanner

雖然 Megastore 慢,但是架不住好用。在 Spanner 論文中提到,2012年大概已經有 300+ 的業務跑在 Megastore 上,在越來越多的業務在 BigTable 上造 ACID Transaction 實現的輪子後,Google 實在受不了了,開始造一個大輪子 Spanner,專案的野心巨大,和 Megastore 一樣,ACID 事務 + 水平擴充套件 + SQL 支援。

但是和 Megastore 不一樣的是,Spanner 沒有選擇在 BigTable 之上構建事務層,而是直接在 Google 的第二代分散式檔案系統 Colossus 之上開始構建 Paxos-replicated tablet。

另外不像 Megastore 實現事務那樣通過各個協調者通過 Paxos 來決定事務的 timestamp,而是引入了硬體,也就是 GPS 時鐘和原子鐘組成的 TrueTime API 來實現事務。這樣一來,不同資料中心發起的事務就不需要跨資料中心協調時間戳,而是直接通過本地資料中心的 TrueTime API 來分配,這樣延遲就降低了很多。

Spanner 近乎完美的一個分散式儲存,在 Google 內部也是的 BigTable 的互補,想做跨資料中心高可用和強一致和事務的話,用 Spanner,代價是可能犧牲一點延遲,但是並沒有Megastore 犧牲那麼多;想高效能(低延遲)的話,用 BigTable。

Google Spanner 目前沒有在 Google Cloud Platform 中提供服務,但是看趨勢簡直是一定的事情,至少作為 Cloud Datastore 的下一代是一定的。另外一方面來看 Google 仍然沒有辦法將 Spanner 開源,原因和 BigTable 一樣,底層依賴了 Colossus 和一堆 Google 內部的元件,另外比 BigTable 更困難的是,TrueTime 是一套硬體。

所以在2012年底釋出 Spanner 的論文後,社群也有開源的實現,比如目前比較成熟的 TiDB 和 CockroachDB,一會提到社群的雲資料庫實現的時候會介紹。Spanner 的介面比 BigTable 稍微豐富一些,支援了它稱之為 Semi-relational 的表結構,可以像關係型資料庫那樣進行 DDL,雖然仍然要指定每行的 primary key,但是比簡單的 kv 還是好太多。

Google F1

在 Spanner 專案開始的同時,Google 啟動了另外一個和 Spanner 配套使用的分散式 SQL 引擎的專案 F1,底層有那麼一個強一致高效能的 Spanner,那麼就可以在上層嘗試將 OLTP 和部分的 OLAP 打通。F1 其實論文題目說是一個資料庫,但是它並不儲存資料,資料都在 Spanner 上,它只是一個分散式查詢引擎,底層依賴 Spanner 提供的事務介面,將使用者的 SQL 請求翻譯成分散式執行計劃。

Google F1 提供了一種可能性,這是在其他的資料庫中一直沒有實現過的:OLTP 與 OLAP 融合的可能性。因為 Google F1 設計目標是給 Google 的廣告系統使用,廣告投放系統這類系統一是對於一致性要求很高,壓力也很大,是典型的 OLTP 場景;第二是可能會有很多複雜的廣告投放效果評估的查詢,而且這類的查詢越是實時越好,這又有點實時 OLAP 的意思。

傳統的做法是 OLTP 的資料庫將資料每隔一段時間同步一份到資料倉儲中,在資料倉儲中離線的進行計算,稍微好點的使用一些流式計算框架進行實時計算。第一種使用資料倉儲的方案,實時性是比較差的,倒騰資料是很麻煩的事情;至於使用流式計算框架的方案,一是靈活性不好,很多查詢邏輯需要提前寫好,沒法做很多 Ad-hoc 的事情,另外因為兩邊是異構的儲存,導致 ETL 也是很麻煩的工作。

F1 其實依靠 Spanner 的 ACID 事務和 MVCC 的特性實現了100%的 OLTP,並且自身作為一個分散式 SQL 引擎,可以利用叢集的計算資源實現分散式的 OLAP 查詢。這帶來的好處就是並不需要額外在設定一個資料倉儲進行資料分析,而是直接在同一個資料庫裡實時分析,另外由於 Spanner 的 MVCC 和多副本帶來的 Lock-free snapshot read 的特性,這類 OLAP 查詢並不會影響正常 OLTP 的操作。

對於 OLTP 來說,瓶頸經常出現在 IO 上。對於 OLAP 來說,瓶頸反而經常出現在 CPU 也就是計算上。其實看上去是能融合起來,提升整個叢集的資源利用率,這也是我看好 Google F1 + Spanner 這個組合的原因,未來的資料庫可能會融合資料倉儲,提供更完整且更實時的體驗。

(其實這個下面的 GFS 不太準確,現在應該是 Colossus)

Open Source Cloud-Native Database

2016年在矽谷突然有個新詞火了起來 GIFEE,Google Infrastructure For Everyone Else,大家意識到好像隨著新一代的開源基礎軟體的繁榮發展,原來在 Google 內部的基礎設施已經有很多高質量的開源實現。

比如容器方面有 Docker,排程器方面 Google 主動開源的 Borg 的第二代 Kubernetes,傳統的 BigTable 和 GFS 社群還有雖然屎但是還是能湊合用的 Hadoop,而且很多大廠覺得 Hadoop 屎的都基本自己都造了類似的輪子……

更別說最近 Google 開源上癮,Kubernetes 就不提了,從大熱的 Tensorflow 到相對冷門但是我個人認為意義重大的 Apache Beam(Google Cloud Dataflow 的基礎),基本能獨立開源的都在積極的擁抱社群。

這就造成了社群與 Google 內部差距正在縮小,但是目前來說,Spanner 和 F1 並不是那麼容易造的。就算拋開 TrueTime 的硬體不提,實現一個穩定的 Multi-Paxos 都不是容易的事情,另外分散式 SQL 優化器這種事情也是有很高技術門檻的,另外就算造出來了,測試的複雜度也一點不比實現的複雜度低(可以參考 PingCAP 的分散式測試哲學的幾篇分享)。

目前從全球範圍內來看,我認為開源世界只有兩個團隊:一個 PingCAP 的 TiDB,一個 CockroachLabs 的 CockroachDB 是有足夠的技術能力和視野能將 Spanner 的開源實現造出來的。目前 TiDB 已經 RC1 並有不少使用者在生產環境使用,比 CockroachDB 的成熟度稍好,架構上更接近正統的 F1 above Spanner 的架構。CockroachDB 的成熟度稍微落後一些,並且協議選擇 PostgreSQL,TiDB 選擇的是 MySQL 的協議相容。

而且從 TiDB 的子專案 TiKV 中,我們看到了新一代分散式 KV 的雛形,RocksDB + Multi-Raft 不依賴第三方分散式檔案系統(DFS)提供水平擴充套件能力,正在成為新一代分散式 KV 儲存標準架構。另外也很欣喜的看到竟然是由一個國內團隊發起並維護的這樣級別的開源專案,即使放到矽谷也是頂級的設計和實現,從 Github 的活躍度和使用的工具及運營社群的流程上來看,很難看出是一個國內團隊。

Kubernetes+Operator

剛才提到了一個詞 Cloud-Native,其實這個詞還沒有準確的定義,不過我的理解是應用開發者和物理設施隔離,也就是業務層不需要再去關心儲存的容量效能等等一切都可以透明水平擴充套件,叢集高度自動化乃至支援自我修復。

對於一個大規模的分散式儲存系統來說人工是很難介入其中的,比如一個上千個節點的分散式系統,幾乎每天都可能有各種各樣的節點故障,瞬時網路抖動甚至整個資料中心直接掛掉,人工去做資料遷移,資料恢復幾乎是不可能的事情。

很多人非常看好 Docker,認為它改變了運維和軟體部署方式,但是我認為更有意義的是 Kubernetes。排程器才是 Cloud-native 架構的核心,容器只是一個載體而已並不重要,Kubernetes 相當於是一個分散式的作業系統,物理層是整個資料中心,也就是 DCOS,這也是我們在 Kubernetes 上下重注的原因,我認為大規模分散式資料庫未來不可能脫離 DCOS。

不過 Kubernetes 上對於有狀態的服務編排是一件比較頭疼的事情。而一般的分散式系統的特點,他不僅每個節點都有儲存的資料,而且他還要根據使用者需要做擴容,縮容。

當程式更新時要可以做到不停服務的滾動升級,當遇到資料負載不均衡情況下系統要做 Rebalance,同時為了保證高可用性,每個節點的資料會有多個副本,當單個節點遇到故障,還需要自動恢復總的副本數。而這些對於 Kubernetes 上的編排一個分散式系統來說都是非常有挑戰的。

Kubernetes 在1.3版本推出了 Petset ,現在已經改名叫 StatefulSet, 核心思想是給 Pod 賦予身份,並且建立和維護 Pod 和 儲存之間的聯絡。當 Pod 可能被排程的時候,對應的 Persistent Volume 能夠跟隨他繫結。但是它並沒有完全解決我們的問題,PS 仍然需要依賴於 Persistent Volume。

目前 Kubernetes 的 Persistent Volume 只提供了基於共享儲存,分散式檔案系統或者 NFS 的實現,還沒有提供 Local Storage 的支援,而且 Petset 本身還處於 Alpha 版本階段,我們還在觀望。

不過除了 Kubernetes 官方社群還是有其他人在嘗試,我們欣喜的看到,就在不久之前,CoreOS 提出了一個新的擴充套件 Kubernetes 的新方法和思路。CoreOS 為 Kubernetes 增加了一個新成員,叫作 Operator。

Operator 其實是一種對 Controller 的擴充套件,具體的實現由於篇幅的原因我就不羅嗦了,簡單來說是一個讓 Kubernetes 排程帶狀態的儲存服務的方案,CoreOS 官方給出了一個 Etcd-cluster 的備份和滾動升級的 operator 實現,我們也在開發 TiDB 的 operator,感興趣的可以關注我們的 Github 瞭解最新的進展。


相關文章