本文是典型分布式系統分析的第三篇,分析的是Bigtable,一個結構化的分布式存儲系統。
Bigtable作為一個分布式存儲系統,和其他分布式系統一樣,需要保證可擴展、高可用與高性能。與此同時,Bigtable還有應用廣泛的特點(wide applicability),既能滿足對延時敏感的、面向終端用戶的應用需求,又能hold住高吞吐需求的批處理程序。
不過,通讀完整篇論文,會發現,Bigtable這個系統是建立在很多其他google的產品上的,如GFS、Chubby。GFS為Bigtable提供了可伸縮、高可靠、高可用的數據存儲服務;而Chubby保證了Bigtable中元數據的高可用、強一致。這種設計思想,跟之前分析過的GFS,以及本人平常使用到的MongoDB不太一樣,在GFS、MongoDB中,元數據服務器一般有兩重功能:維護元數據、集中調度;而Bigtable中的master只負責調度。
本文地址:https://www.cnblogs.com/xybaby/p/9096748.html
Bigtable的定義
Bigtable是06年的論文,當時還是關系型數據庫一統江湖。因此,網上有人說,Bigtable較難以理解,因為Bigtable有一些術語與關系型數據路類似,如row、column、table,但是內部實現、使用方式又與傳統關系型數據庫差異非常之大。不過現在是2018年了,NoSQL已經應用非常廣泛,因此至少現在看起來還是比較容易讀懂的。
A Bigtable is a sparse, distributed, persistent multidimensional sorted map.
上面是Bigtable的定義,特點是sparse、distributed、multidimensional、sorted map,此外,還要加上一個關鍵字:structured。
在文章understanding-hbase-and-bigtable中有對這幾個關鍵字的詳細解釋與舉例。下面結合論文中的例子來分析一下這幾個術語:
圖中,是一個存儲網頁的例子,Bigtable是一個有序的字典(key value pair),key是 (row:string, column:string, time:int64), value則是任意的string。
在網頁存儲這個例子中,row是URL(倒過來的URL,為了讓同一個網站的網頁盡量存放在一起)。column則是由colune family:qualifier組成,上圖中,contens、anchor都是colume family,一個colume family下面可以包含一個到多個colume。time則是不同時刻的版本,基於time,bigtable提供了不同的垃圾回收策略:only last n、only new enough。
Bigtable是結構化(Structured)數據,colume family在定義表(table)的時候就需要創建,類似關系型數據庫。colume family一般數量較少,但colume family下面的colume是動態添加的,數量可以很多。針對上面的例子,有的文章可能只有一個作者,有的文章可能好幾個作者,雖然都有anchor這colume family,但是所包含的colume數量是不一樣的,這也是稱之為Sparse的原因。
Bigtable存儲
Bigtable是一個分布式存儲,可伸縮性(scalability)是首先需要解決的問題,那么Bigtable是如何分片(partition)的呢。
tablet是Bigtable中數據分片和負載均衡的基本單位(the unit of distribution and load balancing.),大小約為100M到200M,其概念等價於GFS、MongoDB中的chunk。簡單來說,就是由連續的若干個row組成的一個區塊,BIgtable維護的是tablet到tablet server的映射關系,當需要遷移數據的時候,也是與tablet為單位。
tablet采用的是range-based的分片方式,相近的row會被划分在同一個tablet里面,range based對於范圍查詢是非常友好的,比如上面網頁存儲的例子,同一個網站的網頁會被盡量放在一起。但是range based容易在寫入的時候流量導入到同一個tablet,需要額外的split來達到均衡。
tablet內部采用了類似LSM(log-Structured merge)Tree的存儲方式,有一個memtable與多個sstable(sorted string table)組成,如下入所示:
上圖分解出了哪些數據是維護在內存中,哪些是持久化到GFS。可以看到memtable是內存中的數據結構,而write ahead log、sstable則會持久化到GFS。
對於memtable,理解比較簡單,就是一個有序的dict,memtable的數據量到達一定情況下的時候就會以sstable的形式寫入到GFS。
sstable定義如下:
a persistent, ordered immutable map from keys to values, where both keys and values are arbitrary byte strings
因此也是順序存儲的,sstable是bigtable數據物理存儲的基本單位。在sstable內部,一個sstable包含多個block(64kb為單位),block index放在sstable末尾,open sstable的時候block index會被加載到內存,二分查找block index就能找到需要的block,加速磁盤讀取。在特殊情況下,sstable也是可以強制放在內存的。
寫操作較為簡單,寫到memtable就可以了。而對於讀操作,則需要merge memtable與SSTable中的數據:
A valid read operation is executed on a merged view of the sequence of SSTables and the memtable.Since the SSTables and the memtable are lexicographically sorted data structures, the merged view can beformed efciently.
由於寫入是在內存中,那么查詢的時候,對於某個key,有可能在memtable中,也有可能在sstable中,而且在哪一個sstable中還是不一定的。舉個簡單的例子,假設一個tablet包含memtable和兩個sstable(第一個sstable比第二個sstable先生成)
第一個sstable
a
k
z
第二個sstable
b
g
y
memtable
c
k
w
查找任何一個key時,需要以(memtable、第二個stable、第一個sstable)的順序查找。比如對於key k,在memtable中找到就可以返回了(雖然第一個sstable也有一個k);對於key g,首先找memtable不命中,然后在第二個sstable命中;對於key m,則查找完所有sstable之后才能知道都不會命中。為了加速查找過程,采用了兩種技術,compaction、bloom filter,前者減少了一次查找讀取sstable的量,后者可以避免在key不存在的時候,無需檢查memtable與sstable。
compaction有幾個層次:
minor compaction: When the memtable size reaches a threshold, the memtable is frozen, a new memtable is created, and the frozen memtable is converted to an SSTable and written to GFS.
merging compaction: reads the contents of a few SSTables and the memtable, and writes out a new SSTable.
major compaction: A merging compaction that rewrites all SSTables into exactly one SSTable
對於LSM-tree的詳細介紹,可以參考DDIA(design data-intensive applications)
Bigtable系統架構
在論文的build blocks部分,提到了Bigtable使用到的其他組件(服務),其中最重要的就是GFS與Chubby,而Bigtable內部又分為三部分:Master,tablet server, client。因此整體架構如下圖(來自slideshare)
Chubby vs master
在Bigtable中,Chubby提供了以下功能:
- to ensure that there is at most one active master at any time; --》任意時刻只有一個master
- to store the bootstrap location of Bigtable data (see Section5.1); --》元數據的起始位置
- to discover tablet servers and finalize tablet server deaths; --》tablet server的生命周期監控
- to store Bigtable schema information (the column family information for each table);
- and to store access control lists.
前三點,在一個獨立的分布式存儲系統(GFS MongoDB)中,應該都是由元數據服務器提供,但在Bigtable中,這部分功能都已到了Chubby,簡化了master本身的設計。
那master的職責就主要是:
- assigning tablets to tablet servers,
- detecting the addition and expiration of tablet servers
- balancing tablet-server load
- garbage collection of files in GFS.
- In addition, it handles schema changes such as table and column family creations.
在經典論文翻譯導讀之《Google File System》一文中,作者總結到:
分布式文件系統常用的架構范式就是“元數據總控+分布式協調調度+分區存儲”。
可以看出這個范式里的兩個角色——協調組件、存儲組件。協調組件負責了元數據總控+分布式協調調度,各存儲組件作為一個分區,負責實際的存儲結構和本地數據讀寫
在Bigtable中,Chubby負責了元數據總控,master負責分布式協調調度。
元數據管理 tablet location
上面提到,Chubby負載元數據總控,那所有tablets的位置信息全都放在Chubby上?顯然是不現實的。
事實上,系統采用了類似B+樹的三層結構來維護tablet location信息
The first level is a file stored in Chubby that contains the location of the root tablet. The root tablet contain the location of all tablets in a special METADATA table.Each METADATA tablet contains the location of a set of user tablets.
可見,Chubby中存儲的只是root tablet的位置信息,數據量很少。在Root tablet里面,維護的是METADATA tablets的位置信息;METADATA tablet存儲的則是應用的tablet的位置信息。
系統也做了一些工作,來減輕存儲METADATA tablets 的 tablet server的負擔,首先METADATA tablet對應的sstable存儲在內存中,無需磁盤操作。其次,bigtable client會緩存元數據信息,而且會prefetch元數據信息,減少交互。
The client library caches tablet locations.
further reduce this cost in the common case by having the client library prefetch tablet locations
單點master
在上圖中可以看出,Bigtable中,master是無狀態的單點,無狀態是指master本身沒有需要持久化的數據。而單點需要面對的問題是單點故障(single point of failure)
首先,master的負載並不高,最重要的原因是,Bigtable client並不與master直接交互(這歸功於master並不維護系統元數據)。而tablets的管理,如創建、遷移,本身就不是高頻操作。
其次,即使master fail(由於crash或者network partition),系統會創建新的master,並在內存中恢復元數據(tablets到tablet server的映射、尚未分配的tablets)。步驟如下:
- The master grabs a unique master lock in Chubby, which prevents concurrent master instantiations.
- The master scans the servers directory in Chubby to find the live servers.
- The master communicates with every live tablet server to discover what tablets are already assigned to each server.
- The master scans the METADATA table to learn the set of tablets. Whenever this scan encounters a tablet that is not already assigned, the master adds the tablet to the set of unassigned tablets, which makes the tablet eligible for tablet assignment.
注意第三 四步,元數據既來自tablet server,又來自METADATA table。一方面是存在有一些尚未分配的tablets(如遷移產生的、talets server故障導致的),這部分只存在於METADATA table;另一方面,tablet server中一定是當前時刻的准確信息。
Bigtable lessons
作為一個划時代的、開創性的、應用廣泛的分布式系統,Bigtable無論在設計、實現、應用中都會遇到很多問題,有很多指的思考、借鑒的地方,他山之石可以攻玉。Bigtable自己總結如下:
(1)萬萬沒想到的失敗和異常
除了大家耳熟能詳的網絡分割(network partition)和節點故障(fail stop)模型,Bigtable還遇到了:
- memory and network corruption,
- large clock skew,
- hung machines,
- extended and asymmetric network partitions,
- bugs in other systems that we are using (Chubby for example),
- oveflow of GFS quotas
- and planned and unplanned hardware maintenance.
(2)三思而后行,不要過度設計
Another lesson we learned is that it is important to delay adding new features until it is clear how the new features will be used.
先搞懂需求背后用戶希望解決的真正問題,有時候需求是假象,需要先挖掘本質
(3)監控的重要性
the importance of proper system-level monitoring(i.e., monitoring both Bigtable itself, as well as the client processes using Bigtable).
不能同意更多,特別是現在服務化、微服務甚囂塵上,沒有完善的監控讓系統的運維苦不堪言。特別是作為各種框架、引擎,完善的監控更是不可或缺。
(4)簡化設計
The most important lesson we learned is the value of simple designs.
在Google三大件(MapReduce、GFS、Bigtable)都提到了這一點,simple design意味着更好的維護性,更少的邊界條件。不過坦白的說,沒有涉及過復雜的系統,是很難體會到"Simple is Better Than Complex"。
雜項
事務支持
分布式系統中,分布式事務會影響到性能、可用性,因此大多只提供單行原子性操作,bigtable中也是如此
Every read or write of data under a single row key is atomic (regardless of the number of different columns being read or written in the row)
locality group
client指定多個colume family形成一個group,locality group單獨存成一個sstable,而且locality group還可以強制保存在內存中,如前面提到的METADATA tablets。
group使用單獨的sstable存儲就使得Bigtable事實上使用了colume based storage,這對於批處理程序或者OLAP非常有用。
Bigtable locality groups realize similar compression and disk read performance benefits observed for other systems that organize data on disk using column-based rather than row-based storage
Merged commit log
為了減輕GFS的負擔,加快commit log 寫入的速度,tablet server並不是為每一個tablets維護一個commit log,而是一個tablet server上的所有tablets公用一個commit file。
但公用的commit log在tablets recover的時候就不又好了,假設某個Tablet server故障,其上維護的諸多tablets會被遷移到其他tablet server上,多個目標tablet server都需要讀取這個commit log文件來恢復tablets的狀態。顯然,都來讀取這個文件是不切實際的,bigtable采取了先對commit log並行歸並排序的算法,讓關聯的數據集中。
We avoid duplicating log reads by first sorting the commit log entries in order of the keys <table; row name; log sequence number>.
In the sorted output, all mutations for a particular tablet are contiguous and can therefore be read efficiently with one disk seek followed by a sequential read.
references
Bigtable: A Distributed Storage System for Structured Data
understanding-hbase-and-bigtable
undestand-google-bigtable-is-as-easy-as-playing-lego-bricks-lecture-by-romain-jacotin
design data-intensive applications