Google NewSQL之Spanner

谷歌分佈式三寶
BigTable、GFS、MapReduce這傳說中的谷歌分佈式三駕馬車,雖然谷歌沒有公開具體實現代碼,但卻公佈了相應論文,對分佈式文件系統、大數據挖掘和NoSQL流行起了重大促進做用,開源界相對應產品是Hbase、HDFS、Hadoop;距谷歌這三篇論文發表已近10年,谷歌內部這三駕馬車也在更新換代:css

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

相關文章
相關標籤/搜索