google三大論文

 Google三大論文之一:BigTable

Bigtable是一個分佈式的結構化數據存儲系統,它被設計用來處理海量數據:通常是分佈在數千臺普通服務器上的PB級的數據。Google的很多項目使用Bigtable存儲數據,包括Web索引、Google Earth、Google Finance。

什麼是BigTable

Bigtable是一個分佈式的結構化數據存儲系統。設計目的是可靠的處理PB級別的數據,並且能夠部署到上千臺機器上。Bigtable已經實現了下面的幾個目標:適用性廣泛、可擴展、高性能和高可用性。

Bigtable看起來像一個數據庫,採用了很多數據庫的實現策略。Bigtable將存儲的數據都視爲字符串,但是Bigtable本身不去解析這些字符串,客戶程序通常會在把各種結構化或者半結構化的數據串行化到這些字符串裏。通過仔細選擇數據的模式,客戶可以控制數據的位置相關性。最後,可以通過BigTable的模式參數來控制數據是存放在內存中、還是硬盤上。

BigTable的數據模型

Bigtable是一個稀疏的、分佈式的、持久化存儲的多維度排序Map。稀疏的意思是,一個表裏不同的行,列可能完完全全不一樣。Bigtable建立在GFS之上本身就意味着分佈式,當然分佈式的意義還不僅限於此。持久化的意思很簡單,Bigtable的數據最終會以文件的形式放到GFS去。

Map的索引是行關鍵字、列關鍵字以及時間戳;
       (row:string,column:string,time:int64)->string

      假設我們想要存儲海量的網頁及相關信息,這些數據可
以用於很多不同的項目,我們姑且稱這個特殊的表爲Webtable。在Webtable裏,我們使用URL作爲行關鍵字,使用網頁的某些屬性作爲列名,網頁的內容存在“contents:”列中,並用獲取該網頁的時間戳作爲標識

 

行:

表中的行關鍵字可以是任意的字符串(目前支持最大64KB的字符串,但是對大多數用戶,10-100個字節就足夠了)。對同一個行關鍵字的讀或者寫操作都是原子的(不管讀或者寫這一行裏多少個不同列). Bigtable通過行關鍵字的字典順序來組織數據。表中的每個行都可以動態分區。每個分區叫做一個”Tablet”,Tablet是數據分佈和負載均衡調整的最小單位。

行是表的一級索引,將列和時間戳合併查詢。如:

        

 

Table{

‘aaaa’:{sth.}

‘bbbb’:{sth.}

‘cccc’:{sth.}     

}

舉例來說,在Webtable裏,通過反轉URL中主機名的方式,可以把同一個域名下的網頁聚集起來組織成連續的行。具體來說,我們可以把maps.google.com/index.html的數據存放在關鍵字com.google.maps/index.html下。把相同的域中的網頁存儲在連續的區域可以讓基於主機和域名的分析更加有效。

列族:

列關鍵字組成的集合叫做“列族“,列族是訪問控制的基本單位。列關鍵字的命名語法如下:列族:限定詞。比如,Webtable有個列族language,language列族用來存放撰寫網頁的語言。我們在language列族中只使用一個列關鍵字,用來存放每個網頁的語言標識ID。Webtable中另一個有用的列族是anchor;這個列族的每一個列關鍵字代表一個錨鏈接,如圖一所示。Anchor列族的限定詞是引用該網頁的站點名;Anchor列族每列的數據項存放的是鏈接文本。

訪問控制、磁盤和內存的使用統計都是在列族層面進行的。列族是訪問表的二級索引,如:

Table{

         ‘aaaa’:{

                   ‘yyyy’:{sth.}

                   ‘zzzz’:{sth.}

         }

         ‘bbbb’:{

                   ‘www’:{sth.}

                   ‘xxxx’:{sth.}

         }

}

 

時間戳:

在Bigtable中,表的每一個數據項都可以包含同一份數據的不同版本;不同版本的數據通過時間戳來索引。Bigtable時間戳的類型是64位整型。

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

時間戳是表的三級索引,數據項中,不同版本的數據按照時間戳倒序排序,即最新的數據排在最前面。如;

Table{

         ‘aaaa’:{

                   12:{sth.}

                   3:{sth.}

         }

         ‘bbbb’:{

                   6:{sth.}

                   2:{sth.}

         }

    ‘cccc’:{

                   7:{sth.}

         }

}

BigTableAPI

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

客戶程序可以對Bigtable進行如下的操作:寫入或者刪除Bigtable中的值、從每個行中查找值、或者遍歷表中的一個數據子集。

Bigtable還支持一些其它的特性,利用這些特性,用戶可以對數據進行更復雜的處理。首先,Bigtable支
持單行上的事務處理,利用這個功能,用戶可以對存儲在一個行關鍵字下的數據進行原子性的讀-更新-寫操作。雖然Bigtable提供了一個允許用戶跨行批量寫入數據的接口,但是,Bigtable目前還不支持通用的跨行事務處理。

BigTable構件

Bigtable是建立在其它的幾個Google基礎構件上的。BigTable使用Google的分佈式文件系統(GFS)存儲日誌文件和數據文件。BigTable集羣通常運行在一個共享的機器池中,池中的機器還會運行其它的各種各樣的分佈式應用程序,BigTable的進程經常要和其它應用的進程共享機器。

BigTable內部存儲數據的文件是GoogleSSTable格式的。從內部看,SSTable是一系列的數據塊(通常每個塊的大小是64KB,這個大小是可以配置的)。SSTable使用塊索引(通常存儲在SSTable的最後)來定位數據塊;在打開SSTable的時候,索引被加載到內存。每次查找都可以通過一次磁盤搜索完成:首先使用二分查找法在內存中的索引裏找到數據塊的位置,然後再從硬盤讀取相應的數據塊。也可以選擇把整個SSTable都放在內存中,這樣就不必訪問硬盤了。

BigTable還依賴一個高可用的、序列化的分佈式鎖服務組件,叫做Chubby。一個Chubby服務包括了5個活動的副本,其中的一個副本被選爲Master,並且處理請求。只有在大多數副本都是正常運行的,並且彼此之間能夠互相通信的情況下,Chubby服務纔是可用的。當有副本失效的時候,Chubby使用Paxos算法來保證副本的一致性。

BigTable的結構介紹

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

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

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

和很多Single-Master類型的分佈式存儲系統類似,客戶端讀取的數據都不經過Master服務器:客戶程序直接和Tablet服務器通信進行讀寫操作。由於BigTable的客戶程序不必通過Master服務器來獲取Tablet的位置信息,因此,大多數客戶程序甚至完全不需要和Master服務器通信。

Tablet分配

在任何一個時刻,一個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提供服務。

Tablet服務

如圖5所示,Tablet的持久化狀態信息保存在GFS上。更新操作提交到REDO日誌中。在這些更新操作中,最近提交的那些存放在一個排序的緩存中,我們稱這個緩存爲memtable;較早的更新存放在一系列SSTable中。爲了恢復一個Tablet,Tablet服務器首先從METADATA表中讀取它的元數據。Tablet的元數據包含了組成這個Tablet的SSTable的列表,以及一系列的RedoPoint這些RedoPoint指向可能含有該Tablet數據的已提交的日誌記錄。Tablet服務器把SSTable的索引讀進內存,之後通過重複RedoPoint之後提交的更新來重建memtable。

當對Tablet服務器進行寫操作時,Tablet服務器首先要檢查這個操作格式是否正確、操作發起者是否有執
行這個操作的權限。權限驗證的方法是通過從一個Chubby文件裏讀取出來的具有寫權限的操作者列表來
進行驗證(這個文件幾乎一定會存放在Chubby客戶緩存裏)。成功的修改操作會記錄在提交日誌裏。

 

 

Google三大論文之二:The Google File System

Google GFS文件系統是一個面向大規模數據密集型應用的、可伸縮的分佈式文件系統。GFS雖然運行在廉價的普遍硬件設備上,但是它依然了提供災難冗餘的能力,爲大量客戶機提供了高性能的服務。

設計思路:

首先,組件失效被認爲是常態事件,而不是意外事件。GFS包括幾百甚至幾千臺普通的廉價設備組裝的存儲機器,同時被相當數量的客戶機訪問。因此有一部分機器宕機也是很常見的事情。

其次,以通常的標準衡量,我們的文件非常巨大。數GB的文件非常普遍。

第三,絕大部分文件的修改是採用在文件尾部追加數據,而不是覆蓋原有數據的方式。對文件的隨機寫入操作在實際中幾乎不存在。一旦寫完之後,對文件的操作就只有讀,而且通常是按順序讀。

第四,應用程序和文件系統API的協同設計提高了整個系統的靈活性。

另外,GFS支持常用的操作,如創建新文件、刪除文件、打開文件、關閉文件、讀和寫文件。且提供了快照和記錄追加操作。

GFS的主要假設如下:

GFS的服務器都是普通的商用計算機,並不那麼可靠,集羣出現結點故障是常態。因此必須時刻監控系統的結點狀態,當結點失效時,必須能檢測到,並恢復之。

系統存儲適當數量的大文件。理想的負載是幾百萬個文件,文件一般都超過100MB,GB級別以上的文件是很常見的,必須進行有效管理。支持小文件,但不對其進行優化。

負載通常包含兩種讀:大型的流式讀(順序讀),和小型的隨機讀。前者通常一次讀數百KB以上,後者通常在隨機位置讀幾個KB。

負載還包括很多連續的寫操作,往文件追加數據(append)。文件很少會被修改,支持隨機寫操作,但不必進行優化。

系統必須實現良好定義的語義,用於多客戶端併發寫同一個文件。同步的開銷必須保證最小。

高帶寬比低延遲更重要,GFS的應用大多需要快速處理大量的數據,很少會嚴格要求單一操作的響應時間。

 

GFS架構:

一個GFS集羣包含一個單獨的Master節點,多臺Chunk服務器,並且同時被多個客戶端訪問,如圖1所示。所有的這些機器通常都是普通的Linux機器,運行着用戶級別(user-level)的服務進程。我們可以很容易的把Chunk服務器和客戶端都放在同一臺機器上,前提是機器資源允許,並且我們能夠接受不可靠的應用程序代碼帶來的穩定性降低的風險。

 

GFS存儲的文件都被分割成固定大小的Chunk。在Chunk創建的時候,Master服務器會給每個Chunk分配一個不變的、全球唯一的64位的Chunk標識。缺省情況下,我們使用3個存儲複製節點,不過用戶可以爲不同的文件命名空間設定不同的複製級別。

Master節點管理所有的文件系統元數據。這些元數據包括名字空間、訪問控制信息、文件和Chunk的映射信息、以及當前Chunk的位置信息。GFS客戶端代碼以庫的形式被鏈接到客戶程序裏。客戶端代碼實現了GFS文件系統的API接口函數、應用程序與Master節點和Chunk服務器通訊、以及對數據進行讀寫操作。

單一Master節點

單一的Master節點的策略大大簡化了我們的設計。單一的Master節點可以通過全局的信息精確定位Chunk的位置以及進行復制決策。另外,我們必須減少對Master節點的讀寫,避免Master節點成爲系統的瓶頸。客戶端並不通過Master節點讀寫文件數據。反之,客戶端向Master節點詢問它應該聯繫的Chunk服務器。

Chunk尺寸:

Chunk的大小是關鍵的設計參數之一。我們選擇了64MB,這個尺寸遠遠大於一般文件系統的Blocksize。每個Chunk的副本都以普通Linux文件的形式保存在Chunk服務器上,只有在需要的時候才擴大。惰性空間分配策略避免了因內部碎片造成的空間浪費,內部碎片或許是對選擇這麼大的Chunk尺寸最具爭議一點。

元數據

GFS是典型的集中式元數據服務,所有的元數據都存放在一個master結點內。元數據主要包括三種:文件和數據塊的命名空間,文件-數據塊映射表,數據塊的副本位置。所有的元數據都是放在內存裏的。

前兩種元數據會被持久化到本地磁盤中,以操作日誌的形式。操作日誌會記錄下這兩種元數據的每一次關鍵變化,因此當master宕機,就可以根據日誌恢復到某個時間點。日誌的意義還在於,它提供了一個時間線,用於定義操作的先後順序,文件、數據塊的版本都依賴於這個時間順序。

數據塊的副本位置則沒有持久化,因爲動輒數以百計的chunkserver是很容易出錯的,因此只有chunkserver對自己存儲的數據塊有絕對的話語權,而master上的位置信息很容易因爲結點失效等原因而過時。取而代之的方法是,master啓動時詢問每個chunkserver的數據塊情況,而且chunkserver在定期的心跳檢查中也會彙報自己存儲的部分數據塊情況。

GFS物理上沒有目錄結構,也不支持鏈接操作,使用一張表來映射文件路徑名和元數據。

操作日誌

操作日誌包含了關鍵的元數據變更歷史記錄。操作日誌非常重要,我們必須確保日誌文件的完整,確保只有在元數據的變化被持久化後,日誌纔對客戶端是可見的。Master服務器在災難恢復時,通過重演操作日誌把文件系統恢復到最近的狀態。爲了縮短Master啓動的時間,我們必須使日誌足夠小。

緩存和預取

GFS的客戶端和chunkserver都不會緩存任何數據,這是因爲GFS的典型應用是順序訪問大文件,不存在時間局部性。空間局部性雖然存在,但是數據集一般很大,以致沒有足夠的空間緩存。

我們知道集中式元數據模型的元數據服務器容易成爲瓶頸,應該儘量減少客戶端與元數據服務器的交互。因此GFS設計了元數據緩存。client需要訪問數據時,先詢問master數據在哪兒,然後將這個數據地址信息緩存起來,之後client對該數據塊的操作都只需直接與chunkserver聯繫了,當然緩存的時間是有限的,過期作廢。

master還會元數據預取。因爲空間局部性是存在,master可以將邏輯上連續的幾個數據塊的地址信息一併發給客戶端,客戶端緩存這些元數據,以後需要時就可以不用找master的麻煩了。

 

 

Google三大論文之三:Google MapReduce

MapReduce是一個編程模型,也是一個處理和生成超大數據集的算法模型的相關實現。用戶首先創建一個Map函數處理一個基於key/value pair的數據集合,輸出中間的基於key/valuepair的數據集合;然後再創建一個Reduce函數用來合併所有的具有相同中間key值的中間value值。

編程模型

MapReduce編程模型的原理是:利用一個輸入key/valuepair集合來產生一個輸出的key/valuepair集合。MapReduce庫的用戶用兩個函數表達這個計算:Map和Reduce。

用戶自定義的Map函數接受一個輸入的key/value pair值,然後產生一箇中間key/valuepair值的集合。MapReduce庫把所有具有相同中間key值I的中間value值集合在一起後傳遞給reduce函數。用戶自定義的Reduce函數接受一箇中間key的值I和相關的一個value值的集合。

Reduce函數合併這些value值,形成一個較小的value值的集合。一般的,每次Reduce函數調用只產生0或1個輸出value值。通常我們通過一個迭代器把中間value值提供給Reduce函數,這樣我們就可以處理無法全部放入內存中的大量的value值的集合。


例如,計算一個大的文檔集合中每個單詞出現的次數,下面是僞代碼段:
map(String key, String value):
// key: document name
// value: document contents
for each word w in value:
EmitIntermediate(w, “1″);
reduce(String key, Iterator values):
// key: a word
// values: a list of counts
int result = 0;
for each v in values:
result += ParseInt(v);
Emit(AsString(result));
Map函數輸出文檔中的每個詞、以及這個詞的出現次數(在這個簡單的例子裏就是1)。Reduce函數把Map函數產生的每一個特定的詞的計數累加起來。

實現步驟:

通過將Map調用的輸入數據自動分割爲M個數據片段的集合,Map調用被分佈到多臺機器上執行。輸入的數據片段能夠在不同的機器上並行處理。使用分區函數將Map調用產生的中間key值分成R個不同分區(例如,hash(key) mod R),Reduce調用也被分佈到多臺機器上執行。分區數量(R)和分區函數由用戶來指定。

 

圖1展示了我們的MapReduce實現中操作的全部流程。當用戶調用MapReduce函數時,將發生下面的一系列動作(下面的序號和圖1中的序號一一對應):
        1.用戶程序首先調用的MapReduce庫將輸入文件分成M個數據片度,每個數據片段的大小一般從 16MB到64MB(可以通過可選的參數來控制每個數據片段的大小)。然後用戶程序在機羣中創建大量的程序副本。
        2.這些程序副本中的有一個特殊的程序–master。副本中其它的程序都是worker程序,由master分配任務。有M個Map任務和R個Reduce任務將被分配,master將一個Map任務或Reduce任務分配給一個空閒的worker。
        3.被分配了map任務的worker程序讀取相關的輸入數據片段,從輸入的數據片段中解析出key/valuepair,然後把key/value pair傳遞給用戶自定義的Map函數,由Map函數生成並輸出的中間key/valuepair,並緩存在內存中。
        4.緩存中的key/valuepair通過分區函數分成R個區域,之後週期性的寫入到本地磁盤上。緩存的key/valuepair在本地磁盤上的存儲位置將被回傳給master,由master負責把這些存儲位置再傳送給Reduceworker。
        5.當Reduceworker程序接收到master程序發來的數據存儲位置信息後,使用RPC從Map worker所在主機的磁盤上讀取這些緩存數據。當Reduceworker讀取了所有的中間數據後,通過對key進行排序後使得具有相同key值的數據聚合在一起。由於許多不同的key值會映射到相同的Reduce任務上,因此必須進行排序。如果中間數據太大無法在內存中完成排序,那麼就要在外部進行排序。
        6.Reduce worker程序遍歷排序後的中間數據,對於每一個唯一的中間key值,Reduce worker程序將這個key值和它相關的中間value值的集合傳遞給用戶自定義的Reduce函數。Reduce函數的輸出被追加到所屬分區的輸出文件。
        7.當所有的Map和Reduce任務都完成之後,master喚醒用戶程序。在這個時候,在用戶程序裏的對MapReduce調用才返回。在成功完成任務之後,MapReduce的輸出存放在R個輸出文件中(對應每個Reduce任務產生一個輸出文件,文件名由用戶指定)。

 

Combiner函數

combiner函數首先在本地將這些記錄進行一次合併,然後將合併的結果再通過
網絡發送出去。Combiner函數在每臺執行Map任務的機器上都會被執行一次。一般情況下,Combiner和Reduce函數是一樣的。Combiner函數和Reduce函數之間唯一的區別是MapReduce庫怎樣控制函數的輸出。Reduce函數的輸出被保存在最終的輸出文件裏,而Combiner函數的輸出被寫到中間文件裏,然後被髮送給Reduce任務。

 

容錯

Worker失敗  

因爲MapReduce庫的設計初衷是使用由成百上千的機器組成的集羣來處理超大規模的數據,所以,這個庫必須要能很好的處理機器故障。worker故障master週期性的ping每個worker。如果在一個約定的時間範圍內沒有收到worker返回的信息,master將把這個worker標記爲失效。所有由這個失效的worker完成的Map任務被重設爲初始的空閒狀態,之後這些任務就可以被安排給其他的worker。同樣的,worker失效時正在運行的Map或Reduce任務也將被重新置爲空閒狀態,等待重新調度。當worker故障時,由於已經完成的Map任務的輸出存儲在這臺機器上,Map任務的輸出已不可訪問了,因此必須重新執行。而已經完成的Reduce任務的輸出存儲在全局文件系統上,因此不需要再次執行。當一個Map任務首先被worker A執行,之後由於worker A失效了又被調度到worker B執行,這個“重新執行”的動作會被通知給所有執行Reduce任務的worker。任何還沒有從worker A讀取數據的Reduce任務將從worker B讀取數據。

master失敗

一個簡單的解決辦法是讓master週期性的將上面描述的數據結構的寫入磁盤,即檢查點(checkpoint)。如果這個master任務失效了,可以從最後一個檢查點(checkpoint)開始啓動另一個master進程。然而,由於只有一個master進程,master失效後再恢復是比較麻煩的,因此我們現在的實現是如果master失效,就中止MapReduce運算。客戶可以檢查到這個狀態,並且可以根據需要重新執行MapReduce操作。在失效方面的處理機制當用戶提供的Map和Reduce操作是輸入確定性函數(即相同的輸入產生相同的輸出)時,我們的分佈式實現在任何情況下的輸出都和所有程序沒有出現任何錯誤、順序的執行產生的輸出是一樣的。我們依賴對Map和Reduce任務的輸出是原子提交的來完成這個特性。每個工作中的任務把它的輸出寫到私有的臨時文件中。每個Reduce任務生成一個這樣的文件,而每個Map任務則生成R個這樣的文件(一個Reduce任務對應一個文件)。當一個Map任務完成的時,worker發送一個包含R個臨時文件名的完成消息給master。如果master從一個已經完成的Map任務再次接收到到一個完成消息,master將忽略這個消息;否則,master將這R個文件的名字記錄在數據結構裏。

 

 

 

MapReduce編程模型在Google內部成功應用於多個領域。我們把這種成功歸結爲幾個方面:首先,由於MapReduce封裝了並行處理、容錯處理、數據本地化優化、負載均衡等等技術難點的細節,這使得MapReduce庫易於使用。即便對於完全沒有並行或者分佈式系統開發經驗的程序員而言;其次,大量不同類型的問題都可以通過MapReduce簡單的解決。比如,MapReduce用於生成Google的網絡搜索服務所需要的數據、用來排序、用來數據挖掘、用於機器學習,以及很多其它的系統;第三,我們實現了一個在數千臺計算機組成的大型集羣上靈活部署運行的MapReduce。這個實現使得有效利用這些豐富的計算資源變得非常簡單,因此也適合用來解決Google遇到的其他很多需要大量計算的問題。

 

 

 

 

 

 

Hadoop實際上就是谷歌三寶的開源實現,Hadoop MapReduce對應Google MapReduce,HBase對應BigTable,HDFS對應GFS。HDFS(或GFS)爲上層提供高效的非結構化存儲服務,HBase(或BigTable)是提供結構化數據服務的分佈式數據庫,Hadoop MapReduce(或Google MapReduce)是一種並行計算的編程模型,用於作業調度。

目前MapReduce已經有多種實現,除了谷歌自己的實現外,還有著名的hadoop,區別是谷歌是c++,而hadoop是用java。另外斯坦福大學實現了一個在多核/多處理器、共享內存環境內運行的MapReduce,稱爲Phoenix(介紹),相關的論文發表在07年的HPCA,是當年的最佳論文哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章