谷歌分布式三寶
BigTable、GFS、MapReduce這傳說中的谷歌分布式三駕馬車,雖然谷歌沒有公開具體實現代碼,但卻公布了相應論文,對分布式文件系統、大數據挖掘和NoSQL流行起了重大促進作用,開源界相對應產品是Hbase、HDFS、Hadoop;距谷歌這三篇論文發表已近10年,谷歌內部這三駕馬車也在更新換代:
BigTable--MegaStore--Spanner、F1
GFS--Colossus
MapReduce--MapReduce、Percolator、Dremel
MegaStore構建在BigTable之上,是個支持同步復制的半關系型數據庫,試圖融合NoSQL和SQL,但寫吞吐量比較差,所以后續又有了Spanner、F1;Colossus就是GFS2代了;而MapReduce卻沒有被替代,只是多了Percolator和Dremel這樣的提供批處理和實時處理的額外補充方案。
我粗糙解讀下谷歌Spanner、F1兩篇論文,希望能窺一窺NewSQL的大概原理,當然Spanner、F1只是NewSQL一種實現方式,另一種實現方式是in-memory方式(如H-Store),這里不作討論;對分布式文件系統Colossus和數據挖掘框架MapReduce等本文也不做討論。
** Spanner和F1是什么?**
Spanner:高可擴展、多版本、全球分布式外加同步復制特性的谷歌內部數據庫,支持外部一致性的分布式事務;設計目標是橫跨全球上百個數據中心,覆蓋百萬臺服務器,包含萬億條行記錄!(Google就是這么霸氣-)
F1: 構建于Spanner之上,在利用Spanner的豐富特性基礎之上,還提供分布式SQL、事務一致性的二級索引等功能,在AdWords廣告業務上成功代替了之前老舊的手工MySQL Shard方案。
** Spanner特性概覽**
1.跨數據中心級的高可用
2.臨時多版本的數據庫,每個數據的版本由commit時間戳來區分
3.支持常見事務
4.支持基于SQL的查詢語言
5.數據中心級的資源透明分配,譬如:a.某個數據中心放什么數據,這樣可以將用戶數據放在離得近的數據中心(有助降低讀延遲);b.各個復制集之間相距距離,這樣話可以將各個數據集盡量靠在一起(有助于降低寫延遲);c.數據中心間可以動態傳輸分配資源,以達負載均衡目的
6.外部一致性的讀寫和全局一致性的讀;這樣可以避免跨數據中心的備份和MapReduce操作出現數據的不一致性
7.原子級全局schema變更(即使當前變更的schema正有事務在執行)
** Spanner的總體架構**
簡單點:Universe > Zone > Spanserver
Universe:Spanner最大的劃分單位,目前全球三個:一個用作測試、一個生產環境、一個開發/生產混合型。它包含很多zone,這些zone由univer master來顯示各自運行狀態,當這些zone之間數據傳輸交流時,是由叫placement driver的東東來負責控制的。
Zone:Zone是Spanner數據復制的最小區間單位,一個數據中心一般包含一個或多個zone;當一個數據需要復制時,可不是說把這個數據復制到第M數據中心的第N臺server,而是發出類似復制到zone X這樣的指令;所以zone具有物理隔離性。一般一個zone含有幾百臺spanserver,有一個zone master來分配哪些數據到哪個
spanserver;同時還有location proxies--一個代理中間件,負責分配哪個spanserver處理對應客戶端請求。
spanserver: 就是一臺物理服務器,沒什么可說的.
spanserver的物理結構
Tablet:最小物理存儲單位。每個spanserver有100~1000個tablet,tablet存儲的是一些映射,如這種表示:
(key:字符串, timestamp:64位的整數)--對應的字符串
你一定說這不就是KV存儲嘛,但請看多了timestamp這個時間戳,因此谷歌說它更像多版本數據庫而不是KV數據庫(個人感覺強詞奪理,哈哈)。Tablet的底層文件存儲方式是B-Tree結構的,此外還有預寫日志文件。
Paxos Group:spanserver之間是通過Paxos協議來復制的,在每個tablet上有個Paxos狀態機,記錄相關tablet的meta信息。Paxos里的leader能一直當下去,只要一直lease下去,每次lease時間默認10s。Paxos里的寫操作要記錄到日志里,而且要記錄兩次,一次寫在tablet里,一次記錄在Paxos本身日志里。寫操作必須要Paxos leader先初始化下,而讀操作可以從其他有數據的replica直接讀,一些這樣的讀寫replica組成了一個Paxos Group。
Directory
前面說了一些tablet+Paxos狀態機可以結合成一個Paxos Group,group之間也有數據交互傳輸,谷歌定義了最小傳輸復制單元directory--是一些有共同前綴的key記錄,這些key也有相同的replica配置屬性。這樣就能很方便的以directory為單位從一個Paxos group移到另一個group里了,速度嘛--一個50M的directory移動需要幾秒。此外當一個Directory過大時Spanner會對它進行分片。
Spanner的數據模型
對開發人員來說,他可不管數據庫的體系結構或物理格式,他需要的就是一個SQL接口抽象,所以數據模型很重要。
Spanner的數據模型由三部分組成:具備同步復制功能的半關系型表、類SQL語言支持、跨行事務支持。有了這層抽象,開發人員就能很容易在schema里建傳統關系型表、使用類SQL查詢,使用事務了。
但嚴格說Spanner的表不是關系型的,倒像KV存儲,因為每張表都需要一個主鍵。1列或幾列組成的主鍵當做key,其他列當做value。你可能會說InnoDB每張表不也需要主鍵,但InnoDB就不是kV存儲呀;不同于InnoDB,InnoDB是非聚簇索引參考聚簇索引(主鍵)形成表內的層次關系,Spanner里是各個表之間都有層次關系,一個表主鍵需引用另個表主鍵組成父子關系.譬如下面例子,雖然語句創建了了user和album兩個表,但實際存儲不是兩個表,而是album參考user主鍵一個KV大表:
Spanner的大殺器--TrueTime API
其實分布式關系型數據庫學術界都提了好多年了,但一直鮮有聽說 ,也是直到近幾年才有MySQL Cluster、NuoDB、VoltDB、OceanBase等產品問世,why?原因在于關系型分布式數據庫實現ACID、分布式事務和分布式join確實有工程上的極大困難,那谷歌又是如何克服的呢其實? 其實Spanner運用的大部分理論也就是常見的2PL、2PC、Paxos等,獨到的地方是就是TrueTime API--我認為本篇論文最有價值的部分。
TrueTime API是什么?是基于GPS和原子鐘的實現,能將各節點的時間不一致縮小控制在10ms以內!因為分布式數據庫對時間要求很高,假如不同地區的數據庫節點出現時間不一致,對一致性復制、恢復都是大麻煩;傳統NTP時間同步由于不太可靠一般不在分布式環境中使用,邏輯時鐘(如lamport時鐘、向量時鐘)又有因果順序、通信開銷等問題,所以谷歌采用了基于GPS和原子鐘的True Time(意味著準確真實的時間!),解決了跨地區分布式節點時間同步問題。
雖然可能實現較復雜,但True Time調用起來卻很簡單,它包含三個方法:
TT.now():當前時間,返回值是一個時間間隔--[起始時間,結束時間];因為誤差總是客觀存在,谷歌沒表達成通常的T+誤差這種形式,而是給了個時間范圍間隔;
TT.after(t):返回True,如果t已經發生過了
TT.before(t):返回True,如果t尚未發生
每個數據中心都有一系列True Time masters,這些master代表著絕對正確的時間,然后每臺機器上的Time slave來從Time master同步。因為單獨GPS或原子鐘都有可能失效(引起失效原因各自不同),為以防萬一所以有這兩種時間確定方式;大部分Time master使用GPS天線接受信號確定時間,另一部Time Master分參考原子鐘的時間。GPS和原子鐘這兩種方式哪個準確率高或者說更值得參考呢?答案是GPS方式,因為隨著時間推移,總歸會有不準確情況,當出現不準確就需要及時從正確源同步以調整,原子鐘廣播的時間時會有一小段1~7ms的不確定的時間漂移誤差,而GPS卻幾乎沒這種情況。
事務并發控制
Spanner支持兩種事務和一種操作:
1.讀寫事務(只寫事務也算此類)
對于最典型的讀寫事務,Spanner使用常見的兩步鎖策略(2PL)來控制并發,并實現了一個所謂的外部一致性:假如事務2在事務1提交后才開始,則事務2提交時間需大于事務1提交時間。
對于寫操作,寫操作都先緩存在客戶端這邊,所以不到此次事務提交,其他讀操作是看不到寫結果的;對于讀操作,Spanner會加讀鎖,并使用wound-wait算法避免死鎖;當讀寫都完畢后,使用兩步提交協議(2PC)進行組提交到其他節點.但2PC協議最怕的就是協調者或參與者宕機導致其他節點漫長的等,Spanner很巧妙的利用Paxos復制協調者和參與者生成的日志到其他副本集,這樣就算協調者或參與者掛掉也有副本上日志可代替讀取.
讀寫事務中比較特別的是更改schema了,在關系型數據庫中這種DDL操作一般會加鎖阻塞讀寫,Spanner能神奇的做到不加鎖無阻塞,其獨到之處是預分配:1.分配一個未來的時間戳t,這樣每個節點都知道在t時刻schema會變,正在訪問這個schema的讀寫操作就會自動同步改變;但如果讀寫操作過了時間戳t,則仍然會阻塞。
2.只讀事務
類似于Innodb的MVCC,Spanner基于True Time實現了多版本的快照隔離級別,可以無鎖讀,也即一個只讀事務不影響其他事務的寫。只要這事務的time確定,哪怕當前正在讀的spanserver壞了,也可以基于True Time從其他replica繼續讀。但也不是任何replica都能提供某個事務的讀的,必須這個replica的"本機時間"大于事務開始的時間,這個“本機時間”是由Paxos狀態機和事務管理器一起決定的。
3.快照讀
只要提供一個已經過去的時間戳,就能在任何replica上讀取這個時間點(段)的數據。
Spanner的性能
spanserver配置: AMD Barcelona 4核CPU(2200MHz)+4G內存
客戶端與測試機網絡延遲: <1ms(因為在同一個數據中心里)
測試數據集: 是由50個Paxos Group、2500個directories組成的
測試方法: 4K讀寫
結果:
1.單臺機器事務commit等待時間5ms,Paxos復制延遲大概9ms;而隨著replica的增多,這兩項數值變化卻不大,當復制規模達到一定程度時,延遲也趨于一穩定值.
2.而對于分布式事務2PC的延遲,則相對較大,谷歌給出了如下測試結果表:
總結:
由于論文的晦澀和本人閱讀的不詳細,很多細節都沒描述出來,但可看出Spanner是一個提供半關系型結構、常見事務支持、類SQL接口且具有高可擴展性、自動分片、同步復制、外部一致性的全球分布式系統;但我感覺這樣還不能算徹底的NewSQL,得再加上基于Spanner的F1才能算作NewSQL,下一篇Google NewSQL之F1會介紹F1的相關內容。
參考資料
[Spanner: Google’s Globally-Distributed Database](http:// research.google.com/archive/spanner-osdi2012.pdf )
Google Spanner原理- 全球級的分布式數據庫
從Google Spanner漫談分布式存儲與數據庫技術
MIT DB Course