【轉】谷歌三大核心技術(三)Google BigTable中文版

 

摘要

Bigtable 是一個分佈式的結構化數據存儲系統,它被設計用來處理海量數據:一般是分佈在數千臺普通服務器上的PB級的數據。Google的不少項目使用 Bigtable存儲數據,包括Web索引、Google Earth、Google Finance。這些應用對Bigtable提出的要求差別很是大,不管是在數據量上(從URL到網頁到衛星圖像)仍是在響應速度上(從後端的批量處理到 實時數據服務)。儘管應用需求差別很大,可是,針對Google的這些產品,Bigtable仍是成功的提供了一個靈活的、高性能的解決方案。本論文描述 了Bigtable提供的簡單的數據模型,利用這個模型,用戶能夠動態的控制數據的分佈和格式;咱們還將描述Bigtable的設計和實現。html

1 介紹

在 過去兩年半時間裏,咱們設計、實現並部署了一個分佈式的結構化數據存儲系統 — 在Google,咱們稱之爲Bigtable。Bigtable的設計目的是可靠的處理PB級別的數據,而且可以部署到上千臺機器上。Bigtable已 經實現了下面的幾個目標:適用性普遍、可擴展、高性能和高可用性。Bigtable已經在超過60個Google的產品和項目上獲得了應用,包括 Google Analytics、Google Finance、Orkut、 PersonalizedSearch、Writely和Google Earth。這些產品對Bigtable提出了迥異的需求,有的須要高吞吐量的批處理,有的則須要及時響應,快速返回數據給最終用戶。它們使用的 Bigtable集羣的配置也有很大的差別,有的集羣只有幾臺服務器,而有的則須要上千臺服務器、存儲幾百TB的數據。程序員

在不少方 面,Bigtable和數據庫很相似:它使用了不少數據庫的實現策略。並行數據庫【14】和內存數據庫【13】已經具有可擴展性和高性能,可是 Bigtable提供了一個和這些系統徹底不一樣的接口。Bigtable不支持完整的關係數據模型;與之相反,Bigtable爲客戶提供了簡單的數據模 型,利用這個模型,客戶能夠動態控制數據的分佈和格式alex注:也就是對BigTable而言,數據是沒有格式的,用數據庫領域的術語說,就是數據沒有Schema,用戶本身去定義Schema),用戶也能夠本身推測(alex注:reasonabout)底層存儲數據的位置相關性(alex注: 位置相關性能夠這樣理解,好比樹狀結構,具備相同前綴的數據的存放位置接近。在讀取的時候,能夠把這些數據一次讀取出來)。數據的下標是行和列的名字,名 字能夠是任意的字符串。Bigtable將存儲的數據都視爲字符串,可是Bigtable自己不去解析這些字符串,客戶程序一般會在把各類結構化或者半結 構化的數據串行化到這些字符串裏。經過仔細選擇數據的模式,客戶能夠控制數據的位置相關性。最後,能夠經過BigTable的模式參數來控制數據是存放在 內存中、仍是硬盤上。web

第二節描述關於數據模型更多細節方面的東西;第三節概要介紹了客戶端API;第四節簡要介紹了BigTable底層使 用的Google的基礎框架;第五節描述了BigTable實現的關鍵部分;第6節描述了咱們爲了提升BigTable的性能採用的一些精細的調優方法; 第7節提供了BigTable的性能數據;第8節講述了幾個Google內部使用BigTable的例子;第9節是咱們在設計和後期支持過程當中獲得一些經 驗和教訓;最後,在第10節列出咱們的相關研究工做,第11節是咱們的結論。正則表達式

2 數據模型

Bigtable是一個稀疏的、分佈式的、持久化存儲的多維度排序Map(alex注:對於程序員來講,Map應該不用翻譯了吧。Map由key和value組成,後面咱們直接使用key和value,再也不另外翻譯了)。Map的索引是行關鍵字、列關鍵字以及時間戳;Map中的每一個value都是一個未經解析的byte數組。算法

 

(row:string,column:string,time:int64)->string數據庫

 

我 們在仔細分析了一個相似Bigtable的系統的種種潛在用途以後,決定使用這個數據模型。咱們先舉個具體的例子,這個例子促使咱們作了不少設計決策;假 設咱們想要存儲海量的網頁及相關信息,這些數據能夠用於不少不一樣的項目,咱們姑且稱這個特殊的表爲Webtable。在Webtable裏,咱們使用 URL做爲行關鍵字,使用網頁的某些屬性做爲列名,網頁的內容存在「contents:」列中,並用獲取該網頁的時間戳做爲標識(alex注:即按照獲取時間不一樣,存儲了多個版本的網頁數據),如圖一所示。編程

圖一:一個存儲Web網頁的例子的表的片段。行名是一個反向URL。contents列族存放的是網頁的內容,anchor列族存放引用該網頁的錨連接文本(alex注:若是不知道HTML的Anchor,請Google一把)。CNN的主頁被Sports Illustrater和MY-look的主頁引用,所以該行包含了名爲「anchor:cnnsi.com」和 「anchhor:my.look.ca」的列。每一個錨連接只有一個版本(alex注:注意時間戳標識了列的版本,t9和t8分別標識了兩個錨連接的版本);而contents列則有三個版本,分別由時間戳t3,t5,和t6標識。後端

表 中的行關鍵字能夠是任意的字符串(目前支持最大64KB的字符串,可是對大多數用戶,10-100個字節就足夠了)。對同一個行關鍵字的讀或者寫操做都是 原子的(無論讀或者寫這一行裏多少個不一樣列),這個設計決策可以使用戶很容易的理解程序在對同一個行進行併發更新操做時的行爲。數組

Bigtable經過行關鍵字的字典順序來組織數據。表中的每一個行均可以動態分區。每一個分區叫作一個」Tablet」,Tablet是 數據分佈和負載均衡調整的最小單位。這樣作的結果是,當操做只讀取行中不多幾列的數據時效率很高,一般只須要不多幾回機器間的通訊便可完成。用戶能夠經過 選擇合適的行關鍵字,在數據訪問時有效利用數據的位置相關性,從而更好的利用這個特性。舉例來講,在Webtable裏,經過反轉URL中主機名的方式, 能夠把同一個域名下的網頁彙集起來組織成連續的行。具體來講,咱們能夠把maps.google.com/index.html的數據存放在關鍵字 com.google.maps/index.html下。把相同的域中的網頁存儲在連續的區域可讓基於主機和域名的分析更加有效。緩存

列族

列 關鍵字組成的集合叫作「列族「,列族是訪問控制的基本單位。存放在同一列族下的全部數據一般都屬於同一個類型(咱們能夠把同一個列族下的數據壓縮在一 起)。列族在使用以前必須先建立,而後才能在列族中任何的列關鍵字下存放數據;列族建立後,其中的任何一個列關鍵字下均可以存放數據。根據咱們的設計意 圖,一張表中的列族不能太多(最多幾百個),而且列族在運行期間不多改變。與之相對應的,一張表能夠有無限多個列。

列關鍵字的命名語法以下:列族:限定詞。列族的名字必須是可打印的字符串,而限定詞的名字能夠是任意的字符串。好比,Webtable有個列族language,language 列族用來存放撰寫網頁的語言。咱們在language列族中只使用一個列關鍵字,用來存放每一個網頁的語言標識ID。Webtable中另外一個有用的列族是 anchor;這個列族的每個列關鍵字表明一個錨連接,如圖一所示。Anchor列族的限定詞是引用該網頁的站點名;Anchor列族每列的數據項存放 的是連接文本。

訪問控制、磁盤和內存的使用統計都是在列族層面進行的。在咱們的Webtable的例子中,上述的控制權限能幫助咱們管理不 同類型的應用:咱們容許一些應用能夠添加新的基本數據、一些應用能夠讀取基本數據並建立繼承的列族、一些應用則只容許瀏覽數據(甚至可能由於隱私的緣由不 能瀏覽全部數據)。

時間戳

在 Bigtable中,表的每個數據項均可以包含同一份數據的不一樣版本;不一樣版本的數據經過時間戳來索引。Bigtable時間戳的類型是64位整型。 Bigtable能夠給時間戳賦值,用來表示精確到毫秒的「實時」時間;用戶程序也能夠給時間戳賦值。若是應用程序須要避免數據版本衝突,那麼它必須本身 生成具備惟一性的時間戳。數據項中,不一樣版本的數據按照時間戳倒序排序,即最新的數據排在最前面。

爲了減輕多個版本數據的管理負擔,咱們對每個列族配有兩個設置參數,Bigtable經過這兩個參數能夠對廢棄版本的數據自動進行垃圾收集。用戶能夠指定只保存最後n個版本的數據,或者只保存「足夠新」的版本的數據(好比,只保存最近7天的內容寫入的數據)。

在Webtable的舉例裏,contents:列存儲的時間戳信息是網絡爬蟲抓取一個頁面的時間。上面說起的垃圾收集機制可讓咱們只保留最近三個版本的網頁數據。

3 API

Bigtable提供了創建和刪除表以及列族的API函數。Bigtable還提供了修改集羣、表和列族的元數據的API,好比修改訪問權限。

 // Open the table

Table *T = OpenOrDie(「/bigtable/web/webtable」);

// Write a new anchor and delete an old anchor

RowMutation r1(T, 「com.cnn.www」);

r1.Set(「anchor:www.c-span.org」, 「CNN」);

r1.Delete(「anchor:www.abc.com」);

Operation op;

Apply(&op, &r1)

Figure 2: Writing to Bigtable.

 

客 戶程序能夠對Bigtable進行以下的操做:寫入或者刪除Bigtable中的值、從每一個行中查找值、或者遍歷表中的一個數據子集。圖2中的C++代碼 使用RowMutation抽象對象進行了一系列的更新操做。(爲了保持示例代碼的簡潔,咱們忽略了一些細節相關代碼)。調用Apply函數對 Webtable進行了一個原子修改操做:它爲www.cnn.com增長了一個錨點,同時刪除了另一個錨點。

 Scanner scanner(T);

ScanStream *stream;

stream = scanner.FetchColumnFamily(「anchor」);

stream->SetReturnAllVersions();

scanner.Lookup(「com.cnn.www」);

for (; !stream->Done(); stream->Next()) {

    printf(「%s %s %lld %s\n」,

    scanner.RowName(),

    stream->ColumnName(),

    stream->MicroTimestamp(),

    stream->Value());

Figure3: Reading from Bigtable.

 

圖 3中的C++代碼使用Scanner抽象對象遍歷一個行內的全部錨點。客戶程序能夠遍歷多個列族,有幾種方法能夠對掃描輸出的行、列和時間戳進行限制。例 如,咱們能夠限制上面的掃描,讓它只輸出那些匹配正則表達式*.cnn.com的錨點,或者那些時間戳在當前時間前10天的錨點。

Bigtable 還支持一些其它的特性,利用這些特性,用戶能夠對數據進行更復雜的處理。首先,Bigtable支持單行上的事務處理,利用這個功能,用戶能夠對存儲在一 個行關鍵字下的數據進行原子性的讀-更新-寫操做。雖然Bigtable提供了一個容許用戶跨行批量寫入數據的接口,可是,Bigtable目前還不支持 通用的跨行事務處理。其次,Bigtable容許把數據項用作整數計數器。最後,Bigtable容許用戶在服務器的地址空間內執行腳本程序。腳本程序使 用Google開發的Sawzall【28】數據處理語言。雖然目前咱們基於的Sawzall語言的API函數還不容許客戶的腳本程序寫入數據到 Bigtable,可是它容許多種形式的數據轉換、基於任意表達式的數據過濾、以及使用多種操做符的進行數據彙總。

Bigtable能夠和MapReduce【12】一塊兒使用,MapReduce是Google開發的大規模並行計算框架。咱們已經開發了一些Wrapper類,經過使用這些Wrapper類,Bigtable能夠做爲MapReduce框架的輸入和輸出。

4 BigTable構件

Bigtable 是創建在其它的幾個Google基礎構件上的。BigTable使用Google的分佈式文件系統(GFS)【17】存儲日誌文件和數據文件。 BigTable集羣一般運行在一個共享的機器池中,池中的機器還會運行其它的各類各樣的分佈式應用程序,BigTable的進程常常要和其它應用的進程 共享機器。BigTable依賴集羣管理系統來調度任務、管理共享的機器上的資源、處理機器的故障、以及監視機器的狀態。

BigTable內部存儲數據的文件是GoogleSSTable格式的。SSTable是一個持久化的、排序的、不可更改的Map結構,而Map是一個key-value 映射的數據結構,key和value的值都是任意的Byte串。能夠對SSTable進行以下的操做:查詢與一個key值相關的value,或者遍歷某個 key值範圍內的全部的key-value對。從內部看,SSTable是一系列的數據塊(一般每一個塊的大小是64KB,這個大小是能夠配置的)。 SSTable使用塊索引(一般存儲在SSTable的最後)來定位數據塊;在打開SSTable的時候,索引被加載到內存。每次查找均可以經過一次磁盤 搜索完成:首先使用二分查找法在內存中的索引裏找到數據塊的位置,而後再從硬盤讀取相應的數據塊。也能夠選擇把整個SSTable都放在內存中,這樣就不 必訪問硬盤了。

BigTable還依賴一個高可用的、序列化的分佈式鎖服務組件,叫作Chubby【8】。一個Chubby服務包括了5個活動的副本,其中的一個副本被選爲Master,而且處理請求。只有在大多數副本都是正常運行的,而且彼此之間可以互相通訊的狀況下,Chubby服 務纔是可用的。當有副本失效的時候,Chubby使用Paxos算法【9,23】來保證副本的一致性。Chubby提供了一個名字空間,裏面包括了目錄和 小文件。每一個目錄或者文件能夠當成一個鎖,讀寫文件的操做都是原子的。Chubby客戶程序庫提供對Chubby文件的一致性緩存。每一個Chubby客戶 程序都維護一個與Chubby服務的會話。若是客戶程序不能在租約到期的時間內從新簽定會話的租約,這個會話就過時失效了(alex注:又用到了lease。原文是:Aclient’s session expires if it is unable to renew its session lease within the leaseexpiration time.)。當一個會話失效時,它擁有的鎖和打開的文件句柄都失效了。Chubby客戶程序能夠在文件和目錄上註冊回調函數,當文件或目錄改變、或者會 話過時時,回調函數會通知客戶程序。

Bigtable使用Chubby完成如下的幾個任務:確保在任何給定的時間內最多隻有一個活動的Master副本;存儲BigTable數據的自引導指令的位置(參考5.1節);查找Tablet服 務器,以及在Tablet服務器失效時進行善後(5.2節);存儲BigTable的模式信息(每張表的列族信息);以及存儲訪問控制列表。若是 Chubby長時間沒法訪問,BigTable就會失效。最近咱們在使用11個Chubby服務實例的14個BigTable集羣上測量了這個影響。因爲 Chubby不可用而致使BigTable中的部分數據不能訪問的平均比率是0.0047%(Chubby不能訪問的緣由多是Chubby自己失效或者 網絡問題)。單個集羣裏,受Chubby失效影響最大的百分比是0.0326%(alex注:有點莫名其妙,原文是: The percentage for the single cluster that was most affected byChubby unavailability was 0.0326%.)。

5 介紹

Bigtable包括了三個主要的組件:連接到客戶程序中的庫、一個Master服務器和多個Tablet服務器。針對系統工做負載的變化狀況,BigTable能夠動態的向集羣中添加(或者刪除)Tablet服務器。

Master服務器主要負責如下工做:爲Tablet服務器分配Tablets、檢測新加入的或者過時失效的Table服務器、對Tablet服務器進行負載均衡、以及對保存在GFS上的文件進行垃圾收集。除此以外,它還處理對模式的相關修改操做,例如創建表和列族。

每一個Tablet服務器都管理一個Tablet的集合(一般每一個服務器有大約數十個至上千個Tablet)。每一個Tablet服務器負責處理它所加載的Tablet的讀寫操做,以及在Tablets過大時,對其進行分割。

和 不少Single-Master類型的分佈式存儲系統【17.21】相似,客戶端讀取的數據都不通過Master服務器:客戶程序直接和Tablet服務 器通訊進行讀寫操做。因爲BigTable的客戶程序沒必要經過Master服務器來獲取Tablet的位置信息,所以,大多數客戶程序甚至徹底不須要和 Master服務器通訊。在實際應用中,Master服務器的負載是很輕的。

一個BigTable集羣存儲了不少表,每一個表包含了一個 Tablet的集合,而每一個Tablet包含了某個範圍內的行的全部相關數據。初始狀態下,一個表只有一個Tablet。隨着表中數據的增加,它被自動分 割成多個Tablet,缺省狀況下,每一個Tablet的尺寸大約是100MB到200MB。

5.1Tablet的位置

咱們使用一個三層的、相似B+樹[10]的結構存儲Tablet的位置信息(如圖4)。

第一層是一個存儲在Chubby中的文件,它包含了Root Tablet的位置信息。Root Tablet包含了一個特殊的METADATA表裏全部的Tablet的位置信息。METADATA表的每一個Tablet包含了一個用戶Tablet的集合。RootTablet其實是METADATA表的第一個Tablet,只不過對它的處理比較特殊 — Root Tablet永遠不會被分割 — 這就保證了Tablet的位置信息存儲結構不會超過三層。

在 METADATA表裏面,每一個Tablet的位置信息都存放在一個行關鍵字下面,而這個行關鍵字是由Tablet所在的表的標識符和Tablet的最後一 行編碼而成的。METADATA的每一行都存儲了大約1KB的內存數據。在一個大小適中的、容量限制爲128MB的METADATA Tablet中,採用這種三層結構的存儲模式,能夠標識2^34個Tablet的地址(若是每一個Tablet存儲128MB數據,那麼一共能夠存儲 2^61字節數據)。

客戶程序使用的庫會緩存Tablet的位置信息。若是客戶程序沒有緩存某個Tablet的地址信息,或者發現它緩存的 地址信息不正確,客戶程序就在樹狀的存儲結構中遞歸的查詢Tablet位置信息;若是客戶端緩存是空的,那麼尋址算法須要經過三次網絡來回通訊尋址,這其 中包括了一次Chubby讀操做;若是客戶端緩存的地址信息過時了,那麼尋址算法可能須要最多6次網絡來回通訊才能更新數據,由於只有在緩存中沒有查到數 據的時候才能發現數據過時(alex注:其中的三次通訊發現緩存過時,另外三次更新緩存數據)(假設METADATA的Tablet沒 有被頻繁的移動)。儘管Tablet的地址信息是存放在內存裏的,對它的操做沒必要訪問GFS文件系統,可是,一般咱們會經過預取Tablet地址來進一步 的減小訪問的開銷:每次須要從METADATA表中讀取一個Tablet的元數據的時候,它都會多讀取幾個Tablet的元數據。

在METADATA表中還存儲了次級信息(alex注:secondary information),包括每一個Tablet的事件日誌(例如,何時一個服務器開始爲該Tablet提供服務)。這些信息有助於排查錯誤和性能分析。

5.2Tablet分配

在 任何一個時刻,一個Tablet只能分配給一個Tablet服務器。Master服務器記錄了當前有哪些活躍的Tablet服務器、哪些Tablet分配 給了哪些Tablet服務器、哪些Tablet尚未被分配。當一個Tablet尚未被分配、而且恰好有一個Tablet服務器有足夠的空閒空間裝載該 Tablet時,Master服務器會給這個Tablet服務器發送一個裝載請求,把Tablet分配給這個服務器。

BigTable使用 Chubby跟蹤記錄Tablet服務器的狀態。當一個Tablet服務器啓動時,它在Chubby的一個指定目錄下創建一個有惟一性名字的文件,而且獲 取該文件的獨佔鎖。Master服務器實時監控着這個目錄(服務器目錄),所以Master服務器可以知道有新的Tablet服務器加入了。若是 Tablet服務器丟失了Chubby上的獨佔鎖 — 好比因爲網絡斷開致使Tablet服務器和Chubby的會話丟失 — 它就中止對Tablet提供服務。(Chubby提供了一種高效的機制,利用這種機制,Tablet服務器可以在不增長網絡負擔的狀況下知道它是否還持有 鎖)。只要文件還存在,Tablet服務器就會試圖從新得到對該文件的獨佔鎖;若是文件不存在了,那麼Tablet服務器就不能再提供服務了,它會自行退 出(alex注:so it killsitself)。當Tablet服務器終止時(好比,集羣的管理系統將運行該Tablet服務器的主機從集羣中移除),它會嘗試釋放它持有的文件鎖,這樣一來,Master服務器就能儘快把Tablet分配到其它的Tablet服務器。

Master 服務器負責檢查一個Tablet服務器是否已經再也不爲它的Tablet提供服務了,而且要儘快從新分配它加載的Tablet。Master服務器經過輪詢 Tablet服務器文件鎖的狀態來檢測什麼時候Tablet服務器再也不爲Tablet提供服務。若是一個Tablet服務器報告它丟失了文件鎖,或者 Master服務器最近幾回嘗試和它通訊都沒有獲得響應,Master服務器就會嘗試獲取該Tablet服務器文件的獨佔鎖;若是Master服務器成功 獲取了獨佔鎖,那麼就說明Chubby是正常運行的,而Tablet服務器要麼是宕機了、要麼是不能和Chubby通訊了,所以,Master服務器就刪 除該Tablet服務器在Chubby上的服務器文件以確保它再也不給Tablet提供服務。一旦Tablet服務器在Chubby上的服務器文件被刪除 了,Master服務器就把以前分配給它的全部的Tablet放入未分配的Tablet集合中。爲了確保Bigtable集羣在Master服務器和 Chubby之間網絡出現故障的時候仍然可使用,Master服務器在它的Chubby會話過時後主動退出。可是無論怎樣,如同咱們前面所描述 的,Master服務器的故障不會改變現有Tablet在Tablet服務器上的分配狀態。

當集羣管理系統啓動了一個Master服務器之 後,Master服務器首先要了解當前Tablet的分配狀態,以後纔可以修改分配狀態。Master服務器在啓動的時候執行如下步驟: (1)Master服務器從Chubby獲取一個惟一的Master鎖,用來阻止建立其它的Master服務器實例;(2)Master服務器掃描 Chubby的服務器文件鎖存儲目錄,獲取當前正在運行的服務器列表;(3)Master服務器和全部的正在運行的Tablet表服務器通訊,獲取每一個 Tablet服務器上Tablet的分配信息;(4)Master服務器掃描METADATA表獲取全部的Tablet的集合。在掃描的過程當中,當 Master服務器發現了一個尚未分配的Tablet,Master服務器就將這個Tablet加入未分配的Tablet集合等待合適的時機分配。

可 能會遇到一種複雜的狀況:在METADATA表的Tablet尚未被分配以前是不可以掃描它的。所以,在開始掃描以前(步驟4),若是在第三步的掃描過 程中發現Root Tablet尚未分配,Master服務器就把Root Tablet加入到未分配的Tablet集合。這個附加操做確保了Root Tablet會被分配。因爲Root Tablet包括了全部METADATA的Tablet的名字,所以Master服務器掃描完Root Tablet之後,就獲得了全部的METADATA表的Tablet的名字了。

保存現有Tablet的集合只有在如下事件發生時纔會改變: 創建了一個新表或者刪除了一箇舊表、兩個Tablet被合併了、或者一個Tablet被分割成兩個小的Tablet。Master服務器能夠跟蹤記錄全部 這些事件,由於除了最後一個事件外的兩個事件都是由它啓動的。Tablet分割事件須要特殊處理,由於它是由Tablet服務器啓動。在分割操做完成之 後,Tablet服務器經過在METADATA表中記錄新的Tablet的信息來提交這個操做;當分割操做提交以後,Tablet服務器會通知 Master服務器。若是分割操做已提交的信息沒有通知到Master服務器(可能兩個服務器中有一個宕機了),Master服務器在要求Tablet服 務器裝載已經被分割的子表的時候會發現一個新的Tablet。經過對比METADATA表中Tablet的信息,Tablet服務器會發現Master服 務器要求其裝載的Tablet並不完整,所以,Tablet服務器會從新向Master服務器發送通知信息。

5.3Tablet服務

如圖5所示,Tablet的持久化狀態信息保存在GFS上。更新操做提交到REDO日誌中(alex注:Updates are committed to a commit log that stores redo records)。 在這些更新操做中,最近提交的那些存放在一個排序的緩存中,咱們稱這個緩存爲memtable;較早的更新存放在一系列SSTable中。爲了恢復一個 Tablet,Tablet服務器首先從METADATA表中讀取它的元數據。Tablet的元數據包含了組成這個Tablet的SSTable的列表, 以及一系列的Redo Point(alex注:a set of redo points),這些Redo Point指向可能含有該Tablet數據的已提交的日誌記錄。Tablet服務器把SSTable的索引讀進內存,以後經過重複Redo Point以後提交的更新來重建memtable。

當 對Tablet服務器進行寫操做時,Tablet服務器首先要檢查這個操做格式是否正確、操做發起者是否有執行這個操做的權限。權限驗證的方法是經過從一 個Chubby文件裏讀取出來的具備寫權限的操做者列表來進行驗證(這個文件幾乎必定會存放在Chubby客戶緩存裏)。成功的修改操做會記錄在提交日誌 裏。能夠採用批量提交方式(alex注:group commit)來提升包含大量小的修改操做的應用程序的吞吐量【13,16】。當一個寫操做提交後,寫的內容插入到memtable裏面。

當對Tablet服務器進行讀操做時,Tablet服務器會做相似的完整性和權限檢查。一個有效的讀操做在一個由一系列SSTable和memtable合併的視圖裏執行。因爲SSTable和memtable是按字典排序的數據結構,所以能夠高效生成合並視圖。

當進行Tablet的合併和分割時,正在進行的讀寫操做可以繼續進行。

5.4Compactions

(alex注:這個詞挺簡單,可是在這節裏面挺難翻譯的。應該是空間縮減的意思,可是彷佛又不能徹底歸納它在上下文中的意思,乾脆,不翻譯了)

隨着寫操做的執行,memtable的大小不斷增長。當memtable的尺寸到達一個門限值的時候,這個memtable就會被凍結,而後建立一個新的memtable;被凍結住memtable會被轉換成SSTable,而後寫入GFS(alex注:咱們稱這種Compaction行爲爲Minor Compaction)。MinorCompaction過程有兩個目的:shrink(alex注:shrink是數據庫用語,表示空間收縮)Tablet服務器使用的內存,以及在服務器災難恢復過程當中,減小必須從提交日誌裏讀取的數據量。在Compaction過程當中,正在進行的讀寫操做仍能繼續。

每 一次Minor Compaction都會建立一個新的SSTable。若是Minor Compaction過程不停滯的持續進行下去,讀操做可能須要合併來自多個SSTable的更新;不然,咱們經過按期在後臺執行Merging Compaction過程合併文件,限制這類文件的數量。Merging Compaction過程讀取一些SSTable和memtable的內容,合併成一個新的SSTable。只要Merging Compaction過程完成了,輸入的這些SSTable和memtable就能夠刪除了。

合併全部的SSTable並生成一個新的 SSTable的Merging Compaction過程叫做Major Compaction。由非Major Compaction產生的SSTable可能含有特殊的刪除條目,這些刪除條目可以隱藏在舊的、可是依然有效的SSTable中已經刪除的數據(alex 注:使人費解啊,原文是SSTables produced by non-major compactions can contain specialdeletion entries that suppress deleted data in older SSTables that are stilllive)。而MajorCompaction過程生成的SSTable不包含已經刪除的信息或數據。 Bigtable循環掃描它全部的Tablet,而且按期對它們執行Major Compaction。Major Compaction機制容許Bigtable回收已經刪除的數據佔有的資源,而且確保BigTable能及時清除已經刪除的數據(alex注:實際是回收資源。數據刪除後,它佔有的空間並不能立刻重複利用;只有空間回收後才能重複使用),這對存放敏感數據的服務是很是重要。

6 優化

上一章咱們描述了Bigtable的實現,咱們還須要不少優化工做才能使Bigtable到達用戶要求的高性能、高可用性和高可靠性。本章描述了Bigtable實現的其它部分,爲了更好的強調這些優化工做,咱們將深刻細節。

局部性羣組

客 戶程序能夠將多個列族組合成一個局部性羣族。對Tablet中的每一個局部性羣組都會生成一個單獨的SSTable。將一般不會一塊兒訪問的列族分割成不一樣的 局部性羣組能夠提升讀取操做的效率。例如,在Webtable表中,網頁的元數據(好比語言和Checksum)能夠在一個局部性羣組中,網頁的內容能夠 在另一個羣組:當一個應用程序要讀取網頁的元數據的時候,它沒有必要去讀取全部的頁面內容。

此外,能夠以局部性羣組爲單位設定一些有用的 調試參數。好比,能夠把一個局部性羣組設定爲所有存儲在內存中。Tablet服務器依照惰性加載的策略將設定爲放入內存的局部性羣組的SSTable裝載 進內存。加載完成以後,訪問屬於該局部性羣組的列族的時候就沒必要讀取硬盤了。這個特性對於須要頻繁訪問的小塊數據特別有用:在Bigtable內部,咱們 利用這個特性提升METADATA表中具備位置相關性的列族的訪問速度。

壓縮

客戶程序能夠控制一個局部性羣組的SSTable是否須要壓縮;若是須要壓縮,那麼以什麼格式來壓縮。每一個SSTable的塊(塊的大小由局部性羣組的優化參數指定)都使用用戶指定的壓縮格式來壓縮。雖然分塊壓縮浪費了少許空間(alex注:相比於對整個SSTable進行壓縮,分塊壓縮壓縮率較低),可是,咱們在只讀取SSTable的一小部分數據的時候就沒必要解壓整個文件了。不少客戶程序使用了「兩遍」的、可定製的壓縮方式。第一遍採用Bentleyand McIlroy’s方式[6],這種方式在一個很大的掃描窗口裏對常見的長字符串進行壓縮;第二遍是採用快速壓縮算法,即在一個16KB的小掃描窗口中尋 找重複數據。兩個壓縮的算法都很快,在如今的機器上,壓縮的速率達到100-200MB/s,解壓的速率達到400-1000MB/s。

雖 然咱們在選擇壓縮算法的時候重點考慮的是速度而不是壓縮的空間,可是這種兩遍的壓縮方式在空間壓縮率上的表現也是使人驚歎。好比,在Webtable的例 子裏,咱們使用這種壓縮方式來存儲網頁內容。在一次測試中,咱們在一個壓縮的局部性羣組中存儲了大量的網頁。針對實驗的目的,咱們沒有存儲每一個文檔全部版 本的數據,咱們僅僅存儲了一個版本的數據。該模式的空間壓縮比達到了10:1。這比傳統的Gzip在壓縮HTML頁面時3:1或者4:1的空間壓縮比好的 多;「兩遍」的壓縮模式如此高效的緣由是因爲Webtable的行的存放方式:從同一個主機獲取的頁面都存在臨近的地方。利用這個特性,Bentley- McIlroy算法能夠歷來自同一個主機的頁面裏找到大量的重複內容。不只僅是Webtable,其它的不少應用程序也經過選擇合適的行名來將類似的數據 聚簇在一塊兒,以獲取較高的壓縮率。當咱們在Bigtable中存儲同一份數據的多個版本的時候,壓縮效率會更高。

經過緩存提升讀操做的性能

爲 了提升讀操做的性能,Tablet服務器使用二級緩存的策略。掃描緩存是第一級緩存,主要緩存Tablet服務器經過SSTable接口獲取的Key- Value對;Block緩存是二級緩存,緩存的是從GFS讀取的SSTable的Block。對於常常要重複讀取相同數據的應用程序來講,掃描緩存很是 有效;對於常常要讀取剛剛讀過的數據附近的數據的應用程序來講,Block緩存更有用(例如,順序讀,或者在一個熱點的行的局部性羣組中隨機讀取不一樣的 列)。

Bloom過濾器

(alex注:Bloom,又叫布隆過濾器,什麼意思?請參考Google黑板報http://googlechinablog.com/2007/07/bloom-filter.html請務必先認真閱讀)

如 5.3節所述,一個讀操做必須讀取構成Tablet狀態的全部SSTable的數據。若是這些SSTable不在內存中,那麼就須要屢次訪問硬盤。咱們通 過容許客戶程序對特定局部性羣組的SSTable指定Bloom過濾器【7】,來減小硬盤訪問的次數。咱們可使用Bloom過濾器查詢一個 SSTable是否包含了特定行和列的數據。對於某些特定應用程序,咱們只付出了少許的、用於存儲Bloom過濾器的內存的代價,就換來了讀操做顯著減小 的磁盤訪問的次數。使用Bloom過濾器也隱式的達到了當應用程序訪問不存在的行或列時,大多數時候咱們都不須要訪問硬盤的目的。

Commit日誌的實現

若是咱們把對每一個Tablet的操做的Commit日誌都存在一個單獨的文件的話,那麼就會產生大量的文件,而且這些文件會並行的寫入GFS。根據GFS服務器底層文件系統實現的方案,要把這些文件寫入不一樣的磁盤日誌文件時(alex注:differentphysicallog files), 會有大量的磁盤Seek操做。另外,因爲批量提交(alex注:groupcommit)中操做的數目通常比較少,所以,對每一個Tablet設置單獨的日 志文件也會給批量提交本應具備的優化效果帶來很大的負面影響。爲了不這些問題,咱們設置每一個Tablet服務器一個Commit日誌文件,把修改操做的 日誌以追加方式寫入同一個日誌文件,所以一個實際的日誌文件中混合了對多個Tablet修改的日誌記錄。

使用單個日誌顯著提升了普通操做的 性能,可是將恢復的工做複雜化了。當一個Tablet服務器宕機時,它加載的Tablet將會被移到不少其它的Tablet服務器上:每一個Tablet服 務器都裝載不多的幾個原來的服務器的Tablet。當恢復一個Tablet的狀態的時候,新的Tablet服務器要從原來的Tablet服務器寫的日誌中 提取修改操做的信息,並從新執行。然而,這些Tablet修改操做的日誌記錄都混合在同一個日誌文件中的。一種方法新的Tablet服務器讀取完整的 Commit日誌文件,而後只重複執行它須要恢復的Tablet的相關修改操做。使用這種方法,假若有100臺Tablet服務器,每臺都加載了失效的 Tablet服務器上的一個Tablet,那麼,這個日誌文件就要被讀取100次(每一個服務器讀取一次)。

爲了不屢次讀取日誌文件,咱們 首先把日誌按照關鍵字(table,row name,log sequence number)排序。排序以後,對同一個Tablet的修改操做的日誌記錄就連續存放在了一塊兒,所以,咱們只要一次磁盤Seek操做、以後順序讀取就能夠 了。爲了並行排序,咱們先將日誌分割成64MB的段,以後在不一樣的Tablet服務器對段進行並行排序。這個排序工做由Master服務器來協同處理,並 且在一個Tablet服務器代表本身須要從Commit日誌文件恢復Tablet時開始執行。

在向GFS中寫Commit日誌的時候可能會 引發系統顛簸,緣由是多種多樣的(好比,寫操做正在進行的時候,一個GFS服務器宕機了;或者鏈接三個GFS副本所在的服務器的網絡擁塞或者過載了)。爲 了確保在GFS負載高峯時修改操做還能順利進行,每一個Tablet服務器實際上有兩個日誌寫入線程,每一個線程都寫本身的日誌文件,而且在任什麼時候刻,只有一 個線程是工做的。若是一個線程的在寫入的時候效率很低,Tablet服務器就切換到另一個線程,修改操做的日誌記錄就寫入到這個線程對應的日誌文件中。 每一個日誌記錄都有一個序列號,所以,在恢復的時候,Tablet服務器可以檢測出並忽略掉那些因爲線程切換而致使的重複的記錄。

Tablet恢復提速

當 Master服務器將一個Tablet從一個Tablet服務器移到另一個Tablet服務器時,源Tablet服務器會對這個Tablet作一次 Minor Compaction。這個Compaction操做減小了Tablet服務器的日誌文件中沒有歸併的記錄,從而減小了恢復的時間。Compaction 完成以後,該服務器就中止爲該Tablet提供服務。在卸載Tablet以前,源Tablet服務器還會再作一次(一般會很快)Minor Compaction,以消除前面在一次壓縮過程當中又產生的未歸併的記錄。第二次Minor Compaction完成之後,Tablet就能夠被裝載到新的Tablet服務器上了,而且不須要從日誌中進行恢復。

利用不變性

我 們在使用Bigtable時,除了SSTable緩存以外的其它部分產生的SSTable都是不變的,咱們能夠利用這一點對系統進行簡化。例如,當從 SSTable讀取數據的時候,咱們沒必要對文件系統訪問操做進行同步。這樣一來,就能夠很是高效的實現對行的並行操做。memtable是惟一一個能被讀 和寫操做同時訪問的可變數據結構。爲了減小在讀操做時的競爭,咱們對內存表採用COW(Copy-on-write)機制,這樣就容許讀寫操做並行執行。

因 爲SSTable是不變的,所以,咱們能夠把永久刪除被標記爲「刪除」的數據的問題,轉換成對廢棄的SSTable進行垃圾收集的問題了。每一個 Tablet的SSTable都在METADATA表中註冊了。Master服務器採用「標記-刪除」的垃圾回收方式刪除SSTable集合中廢棄的 SSTable【25】,METADATA表則保存了Root SSTable的集合。

最後,SSTable的不變性使得分割Tablet的操做很是快捷。咱們沒必要爲每一個分割出來的Tablet創建新的SSTable集合,而是共享原來的Tablet的SSTable集合。

7 性能評估

爲 了測試Bigtable的性能和可擴展性,咱們創建了一個包括N臺Tablet服務器的Bigtable集羣,這裏N是可變的。每臺Tablet服務器配 置了1GB的內存,數據寫入到一個包括1786臺機器、每臺機器有2個IDE硬盤的GFS集羣上。咱們使用N臺客戶機生成工做負載測試Bigtable。 (咱們使用和Tablet服務器相同數目的客戶機以確保客戶機不會成爲瓶頸。)每臺客戶機配置2GZ雙核Opteron處理器,配置了足以容納全部進程工 做數據集的物理內存,以及一張Gigabit的以太網卡。這些機器都連入一個兩層的、樹狀的交換網絡裏,在根節點上的帶寬加起來有大約 100-200Gbps。全部的機器採用相同的設備,所以,任何兩臺機器間網絡來回一次的時間都小於1ms。

Tablet服務器、Master服務器、測試機、以及GFS服務器都運行在同一組機器上。每臺機器都運行一個GFS的服務器。其它的機器要麼運行Tablet服務器、要麼運行客戶程序、要麼運行在測試過程當中,使用這組機器的其它的任務啓動的進程。

R是測試過程當中,Bigtable包含的不一樣的列關鍵字的數量。咱們精心選擇R的值,保證每次基準測試對每臺Tablet服務器讀/寫的數據量都在1GB左右。

在 序列寫的基準測試中,咱們使用的列關鍵字的範圍是0到R-1。這個範圍又被劃分爲10N個大小相同的區間。核心調度程序把這些區間分配給N個客戶端,分配 方式是:只要客戶程序處理完上一個區間的數據,調度程序就把後續的、還沒有處理的區間分配給它。這種動態分配的方式有助於減小客戶機上同時運行的其它進程對 性能的影響。咱們在每一個列關鍵字下寫入一個單獨的字符串。每一個字符串都是隨機生成的、所以也沒有被壓縮(alex注:參考第6節的壓縮小節)。另外,不一樣列關鍵字下的字符串也是不一樣的,所以也就不存在跨行的壓縮。隨機寫入基準測試採用相似的方法,除了行關鍵字在寫入前先作Hash,Hash採用按R取模的方式,這樣就保證了在整個基準測試持續的時間內,寫入的工做負載均勻的分佈在列存儲空間內。

序列讀的基準測試生成列關鍵字的方式與序列寫相同,不一樣於序列寫在列關鍵字下寫入字符串的是,序列讀是讀取列關鍵字下的字符串(這些字符串由以前序列寫基準測試程序寫入)。一樣的,隨機讀的基準測試和隨機寫是相似的。

掃描基準測試和序列讀相似,可是使用的是BigTable提供的、從一個列範圍內掃描全部的value值的API。因爲一次RPC調用就從一個Tablet服務器取回了大量的Value值,所以,使用掃描方式的基準測試程序能夠減小RPC調用的次數。

隨 機讀(內存)基準測試和隨機讀相似,除了包含基準測試數據的局部性羣組被設置爲「in-memory」,所以,讀操做直接從Tablet服務器的內存中讀 取數據,不須要從GFS讀取數據。針對這個測試,咱們把每臺Tablet服務器存儲的數據從1GB減小到100MB,這樣就能夠把數據所有加載到 Tablet服務器的內存中了。

圖6中有兩個視圖,顯示了咱們的基準測試的性能;圖中的數據和曲線是讀/寫 1000-byte value值時取得的。圖中的表格顯示了每一個Tablet服務器每秒鐘進行的操做的次數;圖中的曲線顯示了每秒種全部的Tablet服務器上操做次數的總和。

單個Tablet服務器的性能

咱們首先分析下單個Tablet服務器的性能。隨機讀的性能比其它操做慢一個數量級或以上(alex注:by the order of magnitude or more) 。 每一個隨機讀操做都要經過網絡從GFS傳輸64KB的SSTable到Tablet服務器,而咱們只使用其中大小是1000 byte的一個value值。Tablet服務器每秒大約執行1200次讀操做,也就是每秒大約從GFS讀取75MB的數據。這個傳輸帶寬足以佔滿 Tablet服務器的CPU時間,由於其中包括了網絡協議棧的消耗、SSTable解析、以及BigTable代碼執行;這個帶寬也足以佔滿咱們系統中網 絡的連接帶寬。大多數採用這種訪問模式BigTable應用程序會減少Block的大小,一般會減到8KB。

內存中的隨機讀操做速度快不少,緣由是,全部1000-byte的讀操做都是從Tablet服務器的本地內存中讀取數據,不須要從GFS讀取64KB的Block。

隨 機和序列寫操做的性能比隨機讀要好些,緣由是每一個Tablet服務器直接把寫入操做的內容追加到一個Commit日誌文件的尾部,而且採用批量提交的方 式,經過把數據以流的方式寫入到GFS來提升性能。隨機寫和序列寫在性能上沒有太大的差別,這兩種方式的寫操做實際上都是把操做內容記錄到同一個 Tablet服務器的Commit日誌文件中。

序列讀的性能好於隨機讀,由於每取出64KB的SSTable的Block後,這些數據會緩存到Block緩存中,後續的64次讀操做直接從緩存讀取數據。

掃描的性能更高,這是因爲客戶程序每一次RPC調用都會返回大量的value的數據,因此,RPC調用的消耗基本抵消了。

性能提高

隨 着咱們將系統中的Tablet服務器從1臺增長到500臺,系統的總體吞吐量有了夢幻般的增加,增加的倍率超過了100。好比,隨着Tablet服務器的 數量增長了500倍,內存中的隨機讀操做的性能增長了300倍。之因此會有這樣的性能提高,主要是由於這個基準測試的瓶頸是單臺Tablet服務器的 CPU。

儘管如此,性能的提高還不是線性的。在大多數的基準測試中咱們看到,當Tablet服務器的數量從1臺增長到50臺時,每臺服務器 的吞吐量會有一個明顯的降低。這是因爲多臺服務器間的負載不均衡形成的,大多數狀況下是因爲其它的程序搶佔了CPU。咱們負載均衡的算法會盡可能避免這種不 均衡,可是基於兩個主要緣由,這個算法並不能完美的工做:一個是儘可能減小Tablet的移動致使從新負載均衡能力受限(若是Tablet被移動了,那麼在 短期內 — 通常是1秒內 — 這個Tablet是不可用的),另外一個是咱們的基準測試程序產生的負載會有波動(alex注:the load generated by our benchmarks shifts around as the benchmarkprogresses)

隨 機讀基準測試的測試結果顯示,隨機讀的性能隨Tablet服務器數量增長的提高幅度最小(總體吞吐量只提高了100倍,而服務器的數量卻增長了500 倍)。這是由於每一個1000-byte的讀操做都會致使一個64KB大的Block在網絡上傳輸。這樣的網絡傳輸量消耗了咱們網絡中各類共享的1GB的鏈 路,結果致使隨着咱們增長服務器的數量,每臺服務器上的吞吐量急劇降低。

8 實際應用

截 止到2006年8月,Google內部一共有388個非測試用的Bigtable集羣運行在各類各樣的服務器集羣上,合計大約有24500個Tablet 服務器。表1顯示了每一個集羣上Tablet服務器的大體分佈狀況。這些集羣中,許多用於開發目的,所以會有一段時期比較空閒。經過觀察一個由14個集羣、 8069個Tablet服務器組成的集羣組,咱們看到總體的吞吐量超過了每秒1200000次請求,發送到系統的RPC請求致使的網絡負載達到了 741MB/s,系統發出的RPC請求網絡負載大約是16GB/s。

 

表 2提供了一些目前正在使用的表的相關數據。一些表存儲的是用戶相關的數據,另一些存儲的則是用於批處理的數據;這些表在總的大小、每一個數據項的平均大 小、從內存中讀取的數據的比例、表的Schema的複雜程度上都有很大的差異。本節的其他部分,咱們將主要描述三個產品研發團隊如何使用Bigtable 的。

8.1Google Analytics

Google Analytics是用來幫助Web站點的管理員分析他們網站的流量模式的服務。它提供了總體情況的統計數據,好比天天的獨立訪問的用戶數量、天天每一個 URL的瀏覽次數;它還提供了用戶使用網站的行爲報告,好比根據用戶以前訪問的某些頁面,統計出幾成的用戶購買了商品。

爲了使用這個服 務,Web站點的管理員只須要在他們的Web頁面中嵌入一小段JavaScript腳本就能夠了。這個Javascript程序在頁面被訪問的時候調用。 它記錄了各類Google Analytics須要使用的信息,好比用戶的標識、獲取的網頁的相關信息。Google Analytics彙總這些數據,以後提供給Web站點的管理員。

咱們粗略的描述一下Google Analytics使用的兩個表。Row Click表(大約有200TB數據)的每一行存放了一個最終用戶的會話。行的名字是一個包含Web站點名字以及用戶會話建立時間的元組。這種模式保證了 對同一個Web站點的訪問會話是順序的,會話按時間順序存儲。這個表能夠壓縮到原來尺寸的14%。

Summary表(大約有20TB的數 據)包含了關於每一個Web站點的、各類類型的預約義彙總信息。一個週期性運行的MapReduce任務根據Raw Click表的數據生成Summary表的數據。每一個MapReduce工做進程都從Raw Click表中提取最新的會話數據。系統的總體吞吐量受限於GFS的吞吐量。這個表的可以壓縮到原有尺寸的29%。

8.2Google Earth

Google 經過一組服務爲用戶提供了高分辨率的地球表面衛星圖像,訪問的方式可使經過基於Web的Google Maps訪問接口(maps.google.com),也能夠經過Google Earth定製的客戶端軟件訪問。這些軟件產品容許用戶瀏覽地球表面的圖像:用戶能夠在不一樣的分辨率下平移、查看和註釋這些衛星圖像。這個系統使用一個表 存儲預處理數據,使用另一組表存儲用戶數據。

數據預處理流水線使用一個表存儲原始圖像。在預處理過程當中,圖像被清除,圖像數據合併到最終的服務數據中。這個表包含了大約70TB的數據,因此須要從磁盤讀取數據。圖像已經被高效壓縮過了,所以存儲在Bigtable後不須要再壓縮了。

Imagery 表的每一行都表明了一個單獨的地理區域。行都有名稱,以確保毗鄰的區域存儲在了一塊兒。Imagery表中有一個列族用來記錄每一個區域的數據源。這個列族包 含了大量的列:基本上市每一個列對應一個原始圖片的數據。因爲每一個地理區域都是由不多的幾張圖片構成的,所以這個列族是很是稀疏的。

數據預處理流水線高度依賴運行在Bigtable上的MapReduce任務傳輸數據。在運行某些MapReduce任務的時候,整個系統中每臺Tablet服務器的數據處理速度是1MB/s。

這個服務系統使用一個表來索引GFS中的數據。這個表相對較小(大約是500GB),可是這個表必須在保證較低的響應延時的前提下,針對每一個數據中心,每秒處理幾萬個查詢請求。所以,這個表必須在上百個Tablet服務器上存儲數據,而且使用in-memory的列族。

8.3 個性化查詢

個性化查詢(www.google.com/psearch)是一個雙向服務;這個服務記錄用戶的查詢和點擊,涉及到各類Google的服務,好比Web查詢、圖像和新聞。用戶能夠瀏覽他們查詢的歷史,重複他們以前的查詢和點擊;用戶也能夠定製基於Google歷史使用習慣模式的個性化查詢結果。

個 性化查詢使用Bigtable存儲每一個用戶的數據。每一個用戶都有一個惟一的用戶id,每一個用戶id和一個列名綁定。一個單獨的列族被用來存儲各類類型的行 爲(好比,有個列族多是用來存儲全部的Web查詢的)。每一個數據項都被用做Bigtable的時間戳,記錄了相應的用戶行爲發生的時間。個性化查詢使用 以Bigtable爲存儲的MapReduce任務生成用戶的數據圖表。這些用戶數據圖表用來個性化當前的查詢結果。

個性化查詢的數據會復 制到幾個Bigtable的集羣上,這樣就加強了數據可用性,同時減小了由客戶端和Bigtable集羣間的「距離」形成的延時。個性化查詢的開發團隊最 初創建了一個基於Bigtable的、「客戶側」的複製機制爲全部的複製節點提供一致性保障。如今的系統則使用了內建的複製子系統。

個性化查詢存儲系統的設計容許其它的團隊在它們本身的列中加入新的用戶數據,所以,不少Google服務使用個性化查詢存儲系統保存用戶級的配置參數和設置。在多個團隊之間分享數據的結果是產生了大量的列族。爲了更好的支持數據共享,咱們加入了一個簡單的配額機制(alex注:quota,參考AIX的配額機制)限制用戶在共享表中使用的空間;配額也爲使用個性化查詢系統存儲用戶級信息的產品團體提供了隔離機制。

9 經驗教訓

在設計、實現、維護和支持Bigtable的過程當中,咱們獲得了不少有用的經驗和一些有趣的教訓。

一個教訓是,咱們發現,不少類型的錯誤都會致使大型分佈式系統受損,這些錯誤不只僅是一般的網絡中斷、或者不少分佈式協議中設想的fail-stop類型的錯誤(alex注:fail-stop failture,指一旦系統fail就stop,不輸出任何數據;fail-fastfailture,指fail不立刻stop,在短期內return錯誤信息,而後再stop)。好比,咱們遇到過下面這些類型的錯誤致使的問題:內存數據損壞、網絡中斷、時鐘誤差、機器掛起、擴展的和非對稱的網絡分區(alex注:extended and asymmetric network partitions,不明白什麼意思。partition也有中斷的意思,可是我不知道如何用在這裏)、我 們使用的其它系統的Bug(好比Chubby)、GFS配額溢出、計劃內和計劃外的硬件維護。咱們在解決這些問題的過程當中學到了不少經驗,咱們經過修改協 議來解決這些問題。好比,咱們在咱們的RPC機制中加入了Checksum。咱們在設計系統的部分功能時,不對其它部分功能作任何的假設,這樣的作法解決 了其它的一些問題。好比,咱們再也不假設一個特定的Chubby操做只返回錯誤碼集合中的一個值。

另一個教訓是,咱們明白了在完全瞭解一個 新特性會被如何使用以後,再決定是否添加這個新特性是很是重要的。好比,咱們開始計劃在咱們的API中支持一般方式的事務處理。可是因爲咱們還不會立刻用 到這個功能,所以,咱們並無去實現它。如今,Bigtable上已經有了不少的實際應用,咱們能夠檢查它們真實的需求;咱們發現,大可能是應用程序都只是 須要單個行上的事務功能。有些應用須要分佈式的事務功能,分佈式事務大多數狀況下用於維護二級索引,所以咱們增長了一個特殊的機制去知足這個需求。新的機 制在通用性上比分佈式事務差不少,可是它更有效(特別是在更新操做的涉及上百行數據的時候),並且很是符合咱們的「跨數據中心」複製方案的優化策略。

還 有一個具備實踐意義的經驗:咱們發現系統級的監控對Bigtable很是重要(好比,監控Bigtable自身以及使用Bigtable的客戶程序)。比 如,咱們擴展了咱們的RPC系統,所以對於一個RPC調用的例子,它能夠詳細記錄表明了RPC調用的不少重要操做。這個特性容許咱們檢測和修正不少的問 題,好比Tablet數據結構上的鎖的內容、在修改操做提交時對GFS的寫入很是慢的問題、以及在METADATA表的Tablet不可用時,對 METADATA表的訪問掛起的問題。關於監控的用途的另一個例子是,每一個Bigtable集羣都在Chubby中註冊了。這能夠幫助咱們跟蹤全部的集 羣狀態、監控它們的大小、檢查集羣運行的咱們軟件的版本、監控集羣流入數據的流量,以及檢查是否有引起集羣高延時的潛在因素。

對咱們來講,最寶貴的經驗是簡單設計的價值。考慮到咱們系統的代碼量(大約100000行生產代碼(alex注:non-test code)), 以及隨着時間的推移,新的代碼以各類難以預料的方式加入系統,咱們發現簡潔的設計和編碼給維護和調試帶來的巨大好處。這方面的一個例子是咱們的 Tablet服務器成員協議。咱們初版的協議很簡單:Master服務器週期性的和Tablet服務器簽定租約,Tablet服務器在租約過時的時候 Kill掉本身的進程。不幸的是,這個協議在遇到網絡問題時會大大下降系統的可用性,也會大大增長Master服務器恢復的時間。咱們屢次從新設計這個協 議,直到它可以很好的處理上述問題。可是,更不幸的是,最終的協議過於複雜了,而且依賴一些Chubby不多被用到的特性。咱們發現咱們浪費了大量的時間 在調試一些古怪的問題(alex注:obscure corner cases),有些是Bigtable代碼的問題,有些事Chubby代碼的問題。最後,咱們只好廢棄了這個協議,從新制訂了一個新的、更簡單、只使用Chubby最普遍使用的特性的協議。

10 相關工做

Boxwood 【24】項目的有些組件在某些方面和Chubby、GFS以及Bigtable相似,由於它也提供了諸如分佈式協議、鎖、分佈式Chunk存儲以及分佈式 B-tree存儲。Boxwood與Google的某些組件儘管功能相似,可是Boxwood的組件提供更底層的服務。Boxwood項目的目的是提供創 建相似文件系統、數據庫等高級服務的基礎構件,而Bigtable的目的是直接爲客戶程序的數據存儲需求提供支持。

如今有很多項目已經攻克 了不少難題,實現了在廣域網上的分佈式數據存儲或者高級服務,一般是「Internet規模」的。這其中包括了分佈式的Hash表,這項工做由一些相似 CAN【29】、Chord【32】、Tapestry【37】和Pastry【30】的項目率先發起。這些系統的主要關注點和Bigtable不一樣,比 如應對各類不一樣的傳輸帶寬、不可信的協做者、頻繁的更改配置等;另外,去中心化和Byzantine災難冗餘(alex注:Byzantine, 即拜占庭式的風格,也就是一種複雜詭祕的風格。Byzantine Fault表示:對於處理來講,當發錯誤時處理器並不中止接收輸出,也不中止輸出,錯就錯了,只管算,對於這種錯誤來講,這樣可真是夠麻煩了,由於用戶根 本不知道錯誤發生了,也就根本談不上處理錯誤了。在多處理器的狀況下,這種錯誤可能致使運算正確結果的處理器也產生錯誤的結果,這樣事情就更麻煩了,因此 必定要避免處理器產生這種錯誤。)也不是Bigtable的目的。

就提供給應用程序開發者的分佈式數據存儲模型而言,咱們相信, 分佈式B-Tree或者分佈式Hash表提供的Key-value pair方式的模型有很大的侷限性。Key-value pair模型是頗有用的組件,可是它們不該該是提供給開發者惟一的組件。咱們選擇的模型提供的組件比簡單的Key-value pair豐富的多,它支持稀疏的、半結構化的數據。另外,它也足夠簡單,可以高效的處理平面文件;它也是透明的(經過局部性羣組),容許咱們的使用者對系 統的重要行爲進行調整。

有些數據庫廠商已經開發出了並行的數據庫系統,可以存儲海量的數據。Oracle的RAC【27】使用共享磁盤存儲 數據(Bigtable使用GFS),而且有一個分佈式的鎖管理系統(Bigtable使用Chubby)。IBM並行版本的DB2【4】基於一種相似於 Bigtable的、不共享任何東西的架構(a shared-nothing architecture)【33】。每一個DB2的服務器都負責處理存儲在一個關係型數據庫中的表中的行的一個子集。這些產品都提供了一個帶有事務功能的 完整的關係模型。

Bigtable的局部性羣組提供了相似於基於列的存儲方案在壓縮和磁盤讀取方面具備的性能;這些以列而不是行的方式組織 數據的方案包括C-Store【1,34】、商業產品Sybase IQ【15,36】、SenSage【31】、KDB+【22】,以及MonetDB/X100【38】的ColumnDM存儲層。另一種在平面文件中 提供垂直和水平數據分區、而且提供很好的數據壓縮率的系統是AT&T的Daytona數據庫【19】。局部性羣組不支持Ailamaki系統中描 述的CPU緩存級別的優化【2】。

Bigtable採用memtable和SSTable存儲對錶的更新的方法與Log- StructuredMerge Tree【26】存儲索引數據更新的方法相似。這兩個系統中,排序的數據在寫入到磁盤前都先存放在內存中,讀取操做必須從內存和磁盤中合併數據產生最終的 結果集。

C-Store和Bigtable有不少類似點:兩個系統都採用Shared-nothing架構,都有兩種不一樣的數據結構,一種 用於當前的寫操做,另一種存放「長時間使用」的數據,而且提供一種機制在兩個存儲結構間搬運數據。兩個系統在API接口函數上有很大的不一樣:C- Store操做更像關係型數據庫,而Bigtable提供了低層次的讀寫操做接口,而且設計的目標是可以支持每臺服務器每秒數千次操做。C-Store同 時也是個「讀性能優化的關係型數據庫」,而Bigtable對讀和寫密集型應用都提供了很好的性能。

Bigtable也必須解決全部的 Shared-nothing數據庫須要面對的、類型類似的一些負載和內存均衡方面的難題(好比,【11,35】)。咱們的問題在某種程度上簡單一些: (1)咱們不須要考慮同一份數據可能有多個拷貝的問題,同一份數據可能因爲視圖或索引的緣由以不一樣的形式表現出來;(2)咱們讓用戶決定哪些數據應該放在 內存裏、哪些放在磁盤上,而不是由系統動態的判斷;(3)咱們的系統中沒有複雜的查詢執行或優化工做。

11 結論

我 們已經講述完了Bigtable,Google的一個分佈式的結構化數據存儲系統。Bigtable的集羣從2005年4月開始已經投入使用了,在此之 前,咱們花了大約7人年設計和實現這個系統。截止到2006年4月,已經有超過60個項目使用Bigtable了。咱們的用戶對Bigtable提供的高 性能和高可用性很滿意,隨着時間的推移,他們能夠根據本身的系統對資源的需求增長狀況,經過簡單的增長機器,擴展系統的承載能力。

因爲 Bigtable提供的編程接口並不常見,一個有趣的問題是:咱們的用戶適應新的接口有多難?新的使用者有時不太肯定使用Bigtable接口的最佳方 法,特別是在他們已經習慣於使用支持通用事務的關係型數據庫的接口的狀況下。可是,Google內部不少產品都成功的使用了Bigtable的事實證實 了,咱們的設計在實踐中行之有效。

咱們如今正在對Bigtable加入一些新的特性,好比支持二級索引,以及支持多Master節點的、跨 數據中心複製的Bigtable的基礎構件。咱們如今已經開始將Bigtable部署爲服務供其它的產品團隊使用,這樣不一樣的產品團隊就不須要維護他們自 己的Bigtable集羣了。隨着服務集羣的擴展,咱們須要在Bigtable系統內部處理更多的關於資源共享的問題了【3,5】。

最後, 咱們發現,建設Google本身的存儲解決方案帶來了不少優點。經過爲Bigtable設計咱們本身的數據模型,是咱們的系統極具靈活性。另外,因爲咱們 全面控制着Bigtable的實現過程,以及Bigtable使用到的其它的Google的基礎構件,這就意味着咱們在系統出現瓶頸或效率低下的狀況時, 可以快速的解決這些問題。

相關文章
相關標籤/搜索