bigtable 淺析

     Bigtable 是一個用來管理結構化數據的分佈式存儲系統,具備很好的伸縮性,可以在幾千臺應用服務器上處理PB數量級數據。谷歌有許多項目都把數據存儲在Bigtable中,包括web indexing,Google Earth, and Google Finance. 這些應用對Bigtable的側重點不一樣,可是他們都是海量數據和實時性的應用。儘管需求變化無窮,Bigtable很好的提供了一個靈活多變,高性能額解決方案。
  1. INTRODUCTION
    在不少方面Bigtable都與數據庫相似:他們有一樣的實現策略。並行數據庫和主存數據庫都具備高伸縮性和高性能的特色。可是Bigtable提供了一種不一樣的接個口。Bigtable不支持徹底的關係數據模型;相反,它給客戶端提供了一種簡單的數據模型,這種數據模型支持對數據分佈和格式的動態控制,而且容許客戶端推出底層存儲中數據的分佈特性。Bigtable的數據可使用任意字符的行列進行索引,Bigtable也把數據看成不可解釋的字符串(uninterpreted strings),儘管客戶端經常把不一樣形式的結構化、半結構化的數據序列化造成這些字符。客戶端能夠控制經過進行選擇模式控制數據的位置。最後一點,調整Bigtable的模式參數能讓客戶端動態控制是從內存仍是硬盤提供數據。
 
    2.  DATA MODEL
    一個Bigtable 集羣是一系列運行Bigtable軟件的進程。每個集羣都有一組tables。Bigtable中的表是稀疏的、分佈式、持久的多維有序map。其數據有三個維度:行、列、時間戳。
(row:string, column:string, time:int64) → string
    咱們稱由一個特定行鍵、列鍵、時間戳指定的部分爲一個單元(cell)。多行組合起來造成負載平衡的基本單元,多列組合起來造成訪問控制和資源分配的基本單元。
 
  考慮這樣一個具體的列子:一個大量網頁和相關信息的集合,該集合會被大量不一樣的應用利用。假設咱們稱此表爲Webtable。在Webtable中,URL爲行鍵,網頁的不一樣方面成爲列鍵,存儲網頁的內容。時間戳指的是網頁被獲取的時間。以下圖所示
 
 
    Rows. Bigtable以行鍵的字典序存儲數據,而表中的行鍵是任意的字符串(目前能達到64KB,儘管對於大部分用戶來講10-100字節就夠了)。單行數據的讀寫是串行的(不管該行數據有多少不一樣的列正被讀或者寫),這種設計使得客戶端可以在對某行數據進行並行更新的時候很容易知道系統的行爲。換句話說,行是Bigtable控制事務一致性的基本單元,也就是意味着他不支持跨行事務。
    具備連續鍵值的行組合成 tablets,這是數據分佈和負載平衡的基本單元。這樣的好處是讀取不多的行範圍內的數是高效而且通常僅僅須要和少許的機器交互。客戶端能夠利用這個數據局部性實現高效的數據訪問。例如:在Webtable中,相同域名的網頁分紅一組分佈在相鄰(contiguous)行,存儲的時候把URL的主機部分逆向存儲。maps.google.com/index.html會以com.google.maps/index.html做爲鍵存儲。把相同域名的網頁臨近存放使得某些域名和主機分析更加高效。
    Columns. 列鍵放在一塊兒稱爲列家族,它是訪問控制的基本單元。在一個列族中存放的數據一般是相同類型的。在數據用key存儲以前必須顯式建立列族。在列族建立完成以後,該族任意的列鍵均可以使用:數據能夠在不影響表模式的前提下存儲在這樣的列鍵中。咱們的想法是讓不一樣列族數比較少(最多上百),而且這樣的列族在操做過程當中幾本不會改變;這種限制控制了共享元數據的大小。可是其對列數是沒有任何限制的。
    改變一個表模式可能會刪掉全部的列族,在這種狀況下,該族任意列鍵存儲的數據都將被刪掉。因爲Bigtable並不支持跨行事務,若是數據被存儲在多行,特定的列鍵被刪除,其對應的數據可能不會被刪掉。
    列鍵是用以下的語法命名的:族:標識符。列族的名字必須是可打印的,可是標識符沒有限制。關於Webtable的一個列族例子是網頁編寫的語言。在語言族中咱們僅使用一個列鍵和一個空的標識符來存儲每一個網頁的語言ID。該表另外一個有用的列族是anchor;該族中的每個列鍵都表明一個anchor。標識符就是所指向網址的名字。單元(cell)則包含與連接相關的文本。
    訪問控制以及磁盤內存分配調度都使在列族層次上的。在Webtable例子中,這些控制容許咱們控制幾種不一樣類型的應用:有些應用新增底層數據,有些則讀取底層數據和建立新建的列族,有些僅僅容許訪問已存在的數據(甚至因爲隱私的緣由,不容許訪問全部存在的族的數據)
 
    TimeStamps. 表中不一樣單元格能夠包含一樣數據的不一樣版本,版本是經過timestamp索引的。Bigtable的時間戳是64位整數。他們能夠被Bigtable隱式賦值,這種狀況是「實時的」,精確到毫秒級,或者能夠被客戶端顯式賦值。應用程序必須產生惟一的時間戳來避免衝突。不一樣版本的單元格以降序存儲,這樣最新版本會被最早讀取。
    爲了簡化版本管理,咱們支持兩個per-column-family 告訴Bigtable自動進行垃圾版本回收。客戶端既能夠選擇保存最近的幾個版本,也能夠選擇保存足夠新的版本(例如,僅保存最近七天寫入的)
    在Webtable例子中,咱們能夠把時間戳存儲在扒取網頁的內容中:這列意味着這些網頁版本實際扒取的時間。上面描述的垃圾回收機制使得Bigtable僅保存每一個網頁的最近三個版本。
 
     3 . API
 
    Bigtable的API提供了建立和刪除表和列族的函數。一樣也提供了改變集羣、表和列族元數據的函數,例如訪問控制權限。
    客戶端程序能夠刪除Bigtable中的值或者向Bigtable中寫入數據,從單行中檢索數據,或者對錶中數據子集進行迭代。
 
圖2描述了C++代碼使用RowMutation抽象進行一系列更新操做。(無關細節略去了)調用Apply執行對Webtable的原子操做:向 www.cnn.com增長了一個anchor,同時刪除了一個不一樣的anchor。
 
    圖3 描述了C++使用一個Scanner抽象對某一個特定row的全部anchor進行迭代。客戶機能夠在不一樣的列族進行迭代,不過也有一些機制來限制scan能夠遍歷的行、列、時間戳。例如:咱們能夠限制讓scan僅僅掃描那些匹配正則表達式的列,或者對時間戳進行限制來選擇。
    Bigtable支持不一樣的特性讓用戶可以以複雜多變的方式操做數據。首先,Bigtable支持單行事務,這個特性使得對單行數據能夠執行原子的讀寫序列。Bigtable目前還不遲滯跨行事務,儘管其給客戶機提供了一個接口能夠跨行批量寫入。第二,Bigtable容許單元格充當整數計數器。第三,Bigtable支持客戶端提供的腳本在服務器地址空間中執行。
 
    4.  BUILDING BLOCKS
    Bigtable 是基於其餘幾個Google基本結構的。一個Bigtable集羣一般在運行各類各樣分佈式應用程序的共享機器上運行。Bigtable依賴於谷歌的集羣管理系統來調度任務,管理資源,監控機器狀態,處理異常機器。Bigtable進程與其餘應用程序進程共享機器。如圖4所示,一個Bigtable服務器可能與MapReduce、應用服務器、GFS服務器等運行在同一個機器
 
上。
    Bigtable 使用GFS來存儲日誌和數據文件。GFS是一個分佈式文件系統,能夠保存每一個文件的多個備份以提升可靠性和易用性。
    Bigtable使用Google SSTable不變文件格式存儲Bigtable數據文件。一個SSTable提供了一個持久的有序不變的從key到values的map。用戶能夠經過指定的key查找關聯的value,也能夠對指定的key範圍內數據迭代。每個SSTable都包含了連續的幾個塊(默認狀況下,每一個塊64KB,可是大小是能夠配置的)塊索引(存放在SSTable的最後)是用來定位塊的;當SStable打開時索引就被加載進內存。這樣查詢只須要一次磁盤尋道:首先經過對內存索引進行二分查找找到對應的塊。根據實際狀況,一個SSTable能夠徹底被映射到內存,從而在執行查找和掃描是無需訪問硬盤。
    Bigtable依賴一個高易用性、持久化的分佈式鎖機制——Chubby。一個Chubby服務由五個活動的副本組成,其中一個被選爲主要的用來處理請求。當大部分副本都處於運行狀態而且相通訊時Chubby處於活動狀態,Chubby使用Paxos算法來保證在遇到問題時副本之間的一致性。Chubby還提供了一個由目錄和小文件組成的名字空間。每個目錄或文件均可以看成鎖,讀寫文件都是原子操做。Chubby客戶端庫都提供了對Chubby文件的一致映射。每個Chubby客戶端都和Chubby服務維持一個會話(session).當客戶端session不可以在到期以前續期就會失效,當session失效後,它將失去全部的鎖。Chubby客戶端能夠對文件或目錄註冊一個回調函數以便在session超期或發生改變時接受通知。
    Bigtable的Chubby能夠處理不一樣的任務:保證任什麼時候候之多隻有一個master;存儲Bigtable數據的啓動位置(見5.1);尋找tablet服務器以及肯定tablet服務器的死亡(見5.2);存儲Bigtable模式(見5.5)。若是Chubby服務在一段時間內不可用,Bigtable就會不可用
    5. IMPLEMENTATION
    Bigtable的實現主要包括三哥主要部分:一個連接到每一個客戶端的庫,一個master服務器,許多tablet服務器。Tablet 服務器可動態添加到集羣(或刪除)以適應不一樣的負載。
    master服務器負責把tablets分配到tablet服務器上,檢測tablet服務器的增長和超期,平衡tablet服務器負載,對GFS進行垃圾收集。此外,還能處理模式改變,例如列族的建立和刪除。
    每個tablet服務器都管理一組tablets(通常每一個tablet服務器上有10-1000個tablets)。tablet服務器處理對該服務器上tablets的讀寫請求,也可以將特別大的tablets分紅幾個。
    像許多單master的分佈式存儲系統同樣,客戶端的數據不會移動到master上:客戶端直接與tablet服務器進行讀寫交互。由於Bigtable客戶端無需經過master知道tablet的位置信息,大部分client歷來都不喝master交互。這樣,在實際中,master的負載就會特別小。
    一個Bigtable集羣存儲了大量的tables。每個表都由一組tablets構成,每個tablet包含一個行範圍內的數據。初始狀況下,每一個表僅包含一個tablet。隨着表大小的增加,它會自動分裂成多個tablets,默認每一個表能夠達到1GB。
    儘管,咱們的模型支持任意大小的數據,可是目前的Bigtable的實現還不能很是大的數據。下面的部分江介紹Bigtable實現的細節狀況。
    5.1  Tablet Location
    咱們使用一個相似於B+樹的三層結構存儲位置信息。第一層是存儲在Chubby中的文件,該文件包含root tablet的位置信息。root tablet包含一個特殊METADATA table全部teblets的位置信息.每個tablet包含許多用戶tablets的位置信息。root tablet和其餘tablet有所不一樣——歷來不會分裂——這種特性使得tablet位置層次結構不會超過三層。
    METADATA表用一行存儲一個tablet的位置信息,位置信息包括tablet表標識符的編碼和末行號。每個METADATA行大約佔用1KB的內存。而每一個METADATA表的上限是128MB,這樣咱們三層結構可以處理
2^34的tablets。
    客戶端庫遍歷位置層次結構定位tablets,而且緩存尋找到的tablet的位置。若是client不知道一個tablet的位置,或者它發現它緩存的信息是錯誤的,那麼它將第貴的在位置層次結構中移動。若是客戶端緩存是空的,這種尋找算法須要三次來回傳遞消息,包括一次從Chubby中讀取。若是客戶端緩存中的信息過期了,這中算法須要6次來回消息傳遞才能找到某一個tablet,因爲過期的緩存項只有在miss的狀況下才會發現。儘管tablet位置信息存儲在內存中,無需GFS訪問,咱們經過客戶端庫預取tablet位置信息進一步減小這種一般狀況下的開銷:無論何時客戶端讀取METADATA表時多讀取幾個tablet的metadata。
    咱們一樣在METADATA表中存儲了耳機信息,包括與每個tablet相關的全部事件的日誌(如服務器向其提供服務的時間)這種信息對調試和性能分析的做用是很大的。
 
    5.2  Tablet Assignment
        每個tablet一次至多隻能分配給一個tablet服務器。master服務器跟蹤這些活動的tablet 服務器以及當前正被分配給tablet 服務器的tablet,包括違背分配的tablets.當一個tablet是未分配的,tablet server 有足夠的空間容納一個tablet,master就會經過向tablet server發送一個tablet裝載請求給tablet分配服務器。分配只有在一個master的失效備援工做以前tablet裝載請求尚未收到:由於tablet server 僅接受當前master的裝載請求。所以當一個master服務器發送了一個裝載請求,它能夠假設這個tablet被賦給了某個tablet服務器知道該服務器死亡,或者該tablet服務器通知master它已經卸載了該tablet.
        Bigtable使用Chubby跟蹤這些tablet服務器,當一個tablet服務器啓動時,在一個特定的Chubby目錄下,對一個惟一名字的文件建立一個排它鎖。master監測此目錄來發現tablet服務器。當tablet失去排它鎖時,就會中止對其上的tablets提供服務。例如:網絡中斷可能致使服務器失去和Chubby的會話。tablet服務嘗試從新獲取一文件的排它鎖只要它的文件依舊存在。若是它的文件不存在了,服務器將不能提供任何服務,它就會終結它本身。不論何時tablet服務器終止,它將嘗試釋放本身的鎖,以便master能夠更快的給它的tablets分配新的服務器。
        master負責檢測tablet服務器再也不向tablets提供服務的情形,儘量快的從新分配這些tablets。爲了檢測一個tablet 服務器再也不向tablet提供服務,master週期性詢問每個tablet服務器的鎖的狀態。若是某個tablet服務器告訴master它丟失了它的鎖,或者master幾經嘗試都不可以到達一個tablet服務器.master就會嘗試獲取服務器文件排它鎖。若是master可以獲取該鎖,這說明chubby正常而且tablet服務器要麼終結了要麼不可以到達Chubby.master將會經過刪除它的服務器文件保證該tablet服務器永遠都不可以再提供服務。一旦一個服務器文件被刪除,master就會將以前分配給他的tablets從新變成未分配的。爲了確保master和Chubby之間不易受到網絡問題的影響,master終結本身當它與Chubby之間的會話插旗。master失敗並不會影響tablets分配。
    當一個master被集羣管理系統啓動時,在改變tablet分配以前,它須要知道當前的tablet分配狀況。在啓動時,master會執行如下的步驟:
    (1)首先master會在Chubby中獲取一個爲一個master lock,此鎖能夠避免並行的master實例
    (2)master掃描Chubby的servers目錄發現活動的tablet 服務器
    (3)master與每個活動的tablet server交互發現那些tablets已經分配到每個服務器,更新當前master所瞭解到的信息(任何以前masters發送的tablet裝載請求都會被拒絕)
    (4)master掃描METADATA表知道有哪些tablets.不論何時掃描到一個沒有被分配的tablet,master把這個tablet放到未分配tablet集合中,這樣能夠是得tablet分配更加方便。
        
        一個比較複雜的事情是隻有當METADATA tablets已經被分配以後纔可以掃描METADATA表。所以,在開始掃描以前(第四步),master把root tablet加到未分配tablet集合中若是在第三步中沒有對root tablet進行分配。這會保證root tablet會被分配。由於root tablet包含全部的METADATA表的名字,master在掃描完root tablet以後就會知道全部的METADATA表的名字。
        這些tablets集合只有在表建立或刪除的時候、倆個tablets合併成一個更大的tablet,或者一個tablet分裂成倆個更小的tablets時纔會改變。master可以跟蹤這些改變由於除了最後一個操做,其餘操做都是由master初始化的。對於tablets分裂須要特殊對待,由於這個操做是由tablet servers啓動的。tablet server 分裂過程是這樣的,在METADATA表中記錄新的tablet server的信息。在執行分裂以後,tablet server通知master.若是分裂通知丟失(因爲tablet server或者master死亡)。master會發現該新的tablet當其要求tablet server加載已經分裂的tablet的時候。tablet server會告知master關於該分裂,由於master發如今METADATA表中的tablet項僅能描述它要求加載tablet的一部分信息。
 
    5.3  Tablet Serving
        tablet的持久化信息被存儲在GFS中,如圖6所示
 
        更新信息都被提交到一個存儲恢復記錄的commit log中。最近提交的信息都被存儲在內存中的一個叫作memtable的有序緩衝中。一個memtable保存了row-by-row basis的更新,每一行都經過寫時複製來保證行層次的一致性。更早的更新存儲在一系列的SSTables中(不可改變)
        爲了恢復一個tablet,tablet server從METADATA表中讀取元數據。這些元素據包括組成tablet和一系列的還原點的SSTables,這些元數據可能包含tablet數據的提交日誌的指針。server把SSTables的索引讀進內存,經過還原點中更新記錄重構memetable。
        當tablet server接受到一個寫操做請求,server檢查該請求是不是良定義的,發送者是否有權限執行該操做。檢查權限是經過Chubby文件中一個容許寫的列表(該文件在CHubby客戶端緩存中,幾乎每次都能命中)。合法的改變都會寫入到提交日誌中。批量提交小的改變能夠提升系統吞吐量。當寫操做提交以後,內容就被插入到memtable總。
        當tablet server接收到一個讀操做時,一樣先檢查其形式和權限。
        當tablets正在分裂或者合併的時候讀寫操做仍然能夠繼續。當tablets在被壓縮時,讀寫操做仍然能夠進行。
    5.4  Compactions
        當執行寫操做時,memtable大小增長。當memtable大小達到閾值時memtable就會被凍結,一個新的memtable會被建立,凍結的memtable會被轉換成SSTable並被寫入到GFS中。這種小型壓縮過程有兩個目的:減小tablet server的內存消耗,減小在server死亡狀況下恢復過程當中須要從commit log中讀取的數據量
        每一次微小壓縮(minor compaction)都會建立一個新的SSTable。若是這種行爲不受約束,讀操做可能須要合併任意數量的SSTable中的更新記錄。相反,若是咱們限制了這些文件的數目經過在後臺週期性執行一個合併壓縮。合併壓縮讀取幾個SSTable和memtable的內容,寫入到一個新的SStable中。當壓縮完成後,輸入SSTables和memtable能夠被丟棄掉。
        把全部SSTables中的內容寫入到一個SSTable的合併操做稱之爲大的合併。由小型壓縮產生的SSTables能夠包含特殊的刪除項,該刪除項能夠限制在比較老的SStable中仍然存活着的數據。另外一方面,大的壓縮產生的SSTable不包含任何刪除信息或數據。Bigtable週期性的檢測全部的tablets而且週期性應用大的壓縮。這些大的壓縮讓Bigtable回首被刪除數據使用的水資源,同時也能保證讓需刪除的數據機試從系統中小時,這一點對於存儲敏感數據的服務很重要。
        Bigtable讀性能得以與GFS的局部性優化。當文件被寫入時,GFS嘗試把數據的副本放在寫者的機器上。當讀取GFS文件時,讀取的數據來源於最近可用的副本中。所以,在tablet servers與GFS servers共享機器時,tablet servers會壓縮到在硬盤上有副本的SStables中,這樣能夠在處理連續的讀請求時快速訪問這些SSTables
 
    5.5 模式管理
        BigTable的模式存儲在Chubby中。Chubby給Bigtable模式提供了一個搞笑的交流基質,它提供了對整個文件原子寫操做和小文件的一致性緩存。列入,假設一個客戶端程序想要刪除表中的某些列族。master執行訪問控制檢查,驗證結果模式是梁定義的,而後向Chubby中重寫相應的模式來安裝新的模式。不論何時tablet servers須要決定那些列族存在,僅須要讀取Chubby中相應的模式,這在Chubby的客戶端緩存中經常是存在的。由於Chubby緩存是一致的,tablet servers 必定可以看到對那個文件的全部改變。
 
 
        參考資料:

    Bigtable: A Distributed Storage System for Structured Data

html

相關文章
相關標籤/搜索