HBase是Google的BigTable架構的一個開源實現。但是我個人覺得,要做到充分了解下面兩點還是有點困難的:
一 HBase涵蓋了BigTable規范的哪些部分?
二 HBase與BigTable仍然有哪些區別?
下面我將對這兩個系統做些比較。
在做比較之前,我要指出一個事實:HBase是非常接近BigTable論文描述的東西。撇開一些細微的不同,比如HBase 0.20使用ZooKeeper做它的分布式協調服務,HBase已經基本實現了BigTable所有的功能,所以我下面的篇幅重點落在它們細微的區別上,當然也可以說是HBase小組正在努力改進的地方上。
比較范圍
本文比較的是基于七年前發表的論文(OSDI’06)所描敘的Google BigTable系統,該系統從2005年開始運作。就在論文發表的2006年末到2007年初,作為Hadoop的子項目的HBase也產生了。在那時,HBase的版本是0.15.0. 如今大約2年過去了,Hadoop 0.20.1和HBase 0.20.2都已發布,你當然希望有一些真正的改進。要知道我所比較的是一篇14頁的技術論文和一個從頭到腳都一覽無余的開源項目。所以下面的比較內容里關于HBase怎么做的講得比較多點。
在文章的結尾,我也會討論一些BigTable的如今的新功能,以及HBase跟它們比較如何。好,我們就從術語開始。
術語
有少數幾個不同的術語被兩個系統用來描述同樣的事物。最顯著的莫過于HBase中的regions和BigTable中的tablet。自然地,它們各自把一連串的行(Rows)切分交給許多Region server或者tablet server管理。
特性比較
接下來的就是特性比較列表,列表中是BigTable跟HBase的特性比較。有的是一些實現細節,有的是可配置的選項等。讓人感到有困惑的是,將這些特性分類很難。
特性 |
BigTable
|
HBase
|
說明
|
讀 / 寫 / 修改的原子性 |
支持,每行 |
支持,每行 |
因為 BigTable 不像關系型數據庫,所以不支持事務。最 接近事務的就是讓對每行數據訪問具有原子性。 HBase 同樣實現了”行鎖”的 API ,讓用戶訪問數據時給一行或 者幾行數據加鎖。
|
詞典順序的行排序 |
支持 |
支持 |
所有行都按照詞典順序排序 |
數據塊支持 |
支持 |
支持 |
在數據存儲文件中,數據是由更小的數據塊構成的。這使從大的存儲文件讀取數據更快。數據塊的大小是可 配置的,典型配置是 64K 。
|
數據塊壓縮 |
支持,按Column Family
|
支持,按Column Family
|
Google 使用 BMDiff 和 Zippy 做兩步處理。 BMDiff 工作得很好是因為存儲文件中相鄰的 key-value 對的內容經常非常相似。因為數據支持多個版本,幾個版本的內容會被排序然后被存在一起,它們之間有很 多相同的內容。或者 row key 也會被用這樣的方式處理,比如如果用 URL 來作為 row key ,而這些 URL 來自統一個網站,那么 row key 也會有很多相似之 處。 Zippy 使用的是改進的 LZW 算法。 HBase 使用的是 Java支持的標準的 GZip ,以及一點點 GPL licensed LZO 格式支持。 Hadoop也有想使用 BMDiff 和 Zippy 的征兆。
|
Column Family 數量限制 |
最多幾百 |
小于 100
|
理論上行數和列數是無限的,可是列族( column family )卻不是。這個只是設計上的一些折中考率 .
|
Column Famil命名格式 |
可打印 |
可打印 |
HBase 這樣做的主要原因是 Column Famil 的名稱會被作為文件 系統中的目錄名稱
|
Qualifier 命名的格式 |
任意 |
任意 |
任意的字節數組 |
Key/Value 對的格式 |
任意 |
任意 |
任意的字節數組 |
訪問控制 |
支持 |
無 |
BigTable 支持 column family 級別的訪問控制。 HBase 暫不支持
|
Cell 多版本 |
支持 |
支持 |
多版本支持是基于時間戳。
版本數目限制可以基于 cloumn family 級別自 由配置
|
自定義時間戳 |
支持 |
支持 |
兩個系統都支持用戶設定時間戳,如果用戶不指定,則 使用當前時間作為時間戳。 |
數據 TTL |
支持 |
支持 |
除了數據可以有多個版本,用戶還可制定 TTL ( time-to-live ),當數據到期后會被清除
|
批量寫入 |
支持 |
支持 |
都支持批量表操作 |
值計數器 |
支持 |
支持 |
兩者都可使用特定的列作為原子計數器。 HBase 實現是:當計數器的值要增長時,它必須獲得行鎖。
|
行過濾器 |
支持 |
支持 |
兩者都支持掃描行時支持行過濾器 |
客戶端腳本執行 |
支持 |
不支持 |
BigTable 使用 Sawzall 使客戶端可以處理存儲的數據。
|
MapReduce支持 |
支持 |
支持 |
兩者都有方便的工具類讓 MapReduce Job 掃描 表。
|
底層文件系統 |
GFS |
HDFS,S3, S3N, EBS |
BigTable 工作在 GFS 之上, HBase 可以使用任何文件系統,只要有該文件系統的代理或者驅動即可。
|
存儲文件格式 |
SSTable |
HFile |
|
塊索引 |
在文件最后 |
在文件最后 |
兩者都有相似的塊結構化的存儲文件格式,并且塊索引 被放在文件的最后 |
內存映射 |
支持 |
不支持 |
BigTable 可以讓存儲文件直接映射到內存。
|
鎖服務 |
Chubby |
ZooKeeper |
ZooKeeper 被 HBase 用來協調任務并非當成鎖服務。總體說來, HBase使用 ZooKeeper 達到了 BigTable 使用 Chubby 的效果,只有語義有點細微區別。
|
單個 Master |
是 |
不是 |
HBase 近來支持多個 Master 。多個 Master 是”熱”待命模式工作,它們都偵聽 ZooKeeper 上的 Master 節點。
|
Tablet/Region數目 |
10-1000 |
10-1000 |
兩個系統都推薦每個 Region server 分配相同數目的 region 。當然這決定于很多因素,由于兩個系統都使用普通電腦,出于負載考慮,它們推薦相同的數目
|
Tablet/Region大小 |
100-200MB |
256MB |
在兩個系統中,單個 Region 大小是可配置的,在 HBase 中,默認大小為 256MB
|
Root 位置 |
1st META / Chubby |
-ROOT- / ZooKeeper |
HBase 會使用一個只有單個 Region 的自身表來存儲 Root 表。二者啟動時都會把 Root region 所在機器的 地址放到 ZooKeeper 或者 Chubby中。
|
客戶端Region 信息緩存 |
支持 |
不支持 |
二者客戶端都支持 Region 位置信息緩存并且有相應的機制去除 過時的緩存和更新緩存
|
Meta 預讀 |
支持 |
不支持(?) |
BigTable 的一個設計就是會預讀超過 1 個 Meta Region 信息并將之放入客戶端緩存。
|
Region 事件記錄 |
支持 |
支持 |
Region 相關事件(切分,分配,再分配)都會記錄在 Meta 表中
|
存儲位置分組( Locality Groups ) |
支持 |
不支持 |
這不是很確定,但是看起來 BigTable 中的任何東西都有 個位置分組的屬相。如果多個列族的位置分組相同,那么它們將被存放在一起,并且擁有相同的配置參數。單個列族就可能是一個擁有一個成員的位置分組。 HBase 不支持這種選項,并將不同的列族分開存儲。
|
完全內存Column Family 存儲 |
支持 |
支持 |
這是為需要高速存取小表準備的 |
KeyValue 緩存 |
支持 |
不支持 |
緩存熱點 Cell 數據
|
數據塊緩存 |
支持 |
支持 |
數據塊從存儲文件讀入到在可配置的緩存中 |
布隆過濾器(Bloom Filters) |
支持 |
支持 |
這些過濾器會消耗一些內存,但是可以快速檢查一個指定的 cell 是否在一個 Region Server 上存在
|
Write-Ahead Log (WAL) |
支持 |
支持 |
每個 Region Server 都會記錄被它管理的所有 Region 上的數據改動
|
Secondary Log |
支持 |
不支持 |
出于性能考慮,一旦 WAL 性能下降, BigTable 還有別的 log 可以使用
|
忽略 Write-Ahead Log |
? |
支持 |
在大量數據導入時, HBase 的客戶端可以選擇忽略 WAL
|
快速 Region切分 |
支持 |
支持 |
切分 region 是快速的,因為切分出來的子 region 暫時還會去讀取原存儲文件直到一個 compaction 將數據寫入 region 的自有的存儲文件
|
BigTable 新特性
OSDI’06 BigTable論文發表已有幾年,BigTable當然也有改進。杰夫.迪恩—一個在Google的家伙在近來的一些演講和演示中提到了 BigTable的新特性。我們就來瞧瞧部分新特性吧。
特性 |
BigTable |
HBase |
說明 |
客戶端隔離 |
支持 |
不支持 |
BigTable 可以內在地被用來服務很多單獨的客戶端,并且使它們的數據隔離不互相影響
|
協同處理(Coprocessors )
|
支持 |
暫不支持 |
BigTable 在 region 中運行的代碼可以隨著 region 的被切分,代碼也被會切分到新的 region 上運行。
|
數據錯誤安全 |
支持 |
不支持 |
BigTable 使用 CRC 校驗碼確認數據是否被安全寫入。 HBase 沒有這個特性,問題是: Hadoop 是否會包含這個特性?
|
數據中心間數據復制 |
支持 |
暫不支持 |
HBase 的一個 issue : HBASE-1295 就是關于這個特性的。
|
變化和差異
上面討論的一些特性比較可以看出有些特性差異并不是可以簡單歸結為”是或否”類的問題,對這類問題我將在下面單獨探討。
鎖服務
下面的來自BigTable論文
BigTable用Chubby來完成一些不同的任務:保證在任何時候只有一個活動的Master;存儲BigTable引導區地址;發現tablet server以及在table server死亡時做善后工作;存儲BigTable的schema信息(每個表的列族信息);存儲訪問控制列表。如果Chubby在一段較長的時候變得不可用,那么BigTable系統就會變得不可用。
BigTable如何使用Chubby跟HBase如何使用ZooKeeper有很多異曲同工之處。但有一個區別就是:HBase并不把 Schema信息存儲在ZooKeeper中。它們都非常依賴鎖服務的正常運作。根據我自身的經驗以及我閱讀HBase郵件列表所得到的,我們經常低估當 ZooKeeper無法取得足夠的資源去作出實時回應時的后果。寧可讓ZooKeeper集群運行在相對較老舊的但是什么別的事都不干的機器上,而不是運 行在已被Hadoop或者HBase進程搞得不堪重負的機器上。一旦你的ZooKeeper沒有足夠的資源提供服務,就會引發多米諾骨式的效 應,HBase將會掛掉—包括master節點。
更新:在跟ZooKeeper開發小組討論后,我想指出的是這并不真正意義上是ZooKeeper的一個問題。因為如果運行ZooKeeper的機 器負荷很重,那么存取ZooKeeper上的資源很可能會超時。在這種情形下,HBase的Region Server甚至Master可能會認為協調服務已經壞了,它們就會讓自己停工關閉。 帕特里克 . 亨特已經 通過郵件和發帖對此作出回應。你可以讀他的郵件或者帖子,然后檢查自己的ZooKeeper 是否有能力處理負荷。我個人建議是 將 ZooKeeper 集群跟 HBase 集群分開。你可以把 ZooKeeper 集群運行在一組空閑的稍微有點過時 但是性能還相當不錯的機器上。這樣你可以單獨監控 ZooKeeper 集群和 HBase 集群中的機器,而不必有以下的煩惱:當一個機器的 CPU 負荷 100% 的 時候,你搞不清楚這個負荷究竟來自哪個進程或者有什么后果。
另外一個重要區別是: ZooKeeper 并不是一個像 Chubby 一樣的鎖服務系統,但是目前為止,這并不是 HBase 所 關心的。 ZooKeeer 提供一個分布式的協調服務,讓 HBase 可以選舉出 Master 節 點。它也可以提供用以表示狀態或者某個動作需要的信號量。當 Chubby 生成一個鎖文件來表示一個 tablet 活動的,與此相對應的一個 Region server 會在 ZooKeeper 中生成一個節點來表示自己的存在。這個節點創建以后,只要 ZooKeeper 不掛,它會一直存在。在 BigTable 中,當一個 tablet server 的鎖文件被刪除時就表示與這個 tablet server 的租約失效。在 HBase 中,因為 ZooKeeper 相對少點限制的架構,這種行為會被處理得有所不同。它們只是語義上有所差別,并不意味著誰優誰劣,僅僅有所不同而已。
在Chubby中,第一層級的文件包含著根tablet的位置信息。根tablet包含著一個特別的名叫METADATA(元數據)表的所有的tablet的位置信息。每個METADATA的tablet包含著一組用戶表的tablet的位置信息。根tablet僅僅是METADATA表中第一個tablet,但是它被特別的看待—它從不會被切分,這是為了保證tablet的位置層級不會超過3層。
就如上面所說的,在HBase中,根region是一個只有單個region的表。要說有什么區別的話,那就是根region并不是meta表中的第一個 不可切分的region。它們是相同的功能,只是實現上有差別。
METADATA表存儲著tablet的位置信息,跟在起始row key和末尾row key以及表名之后。
HBase的做法有點不同,它也會為每個region存儲起始的row key和末尾row key,但是末尾的row key并不是屬于當前的region的,它會是另一個region的起始row key.
Master的行為
為了偵測一個tablet server是否不再為它的tablets服務,master會定期地查看每個tablet server鎖文件的狀態。當一個tablet server報告它丟失了鎖文件,或者master經過幾次嘗試后未能聯系上tablet server, master就會嘗試在tablet server的鎖文件上獲得獨占鎖。如果master可以獲得這個鎖,還有Chubby是良好工作的,這時如果tablet server已經死亡或者已經把錯誤上報給Chubby,這時master就可以確定該tablet server不可能恢復,于是會刪除它的鎖文件。一旦一個tablet server的鎖文件被刪除,本來被該tablet server服務的tablets會被移到沒被分配的tablets集合中去。為了保證master和Chubby之間網絡暢通,只要master的Chubby session過期,master將會自殺。
直到0.20.2版本,HBase的行為都相當不同。Region server利用heartbeat協議給master定期報告,master接到報告就知道region server還活著。
Master啟動
Master啟動有以下步驟:(1)Master從Chubby中獲取唯一的master鎖,用來防止多個master的初始化(2)Master 掃描Chubby中的server目錄找到活動的server.(3) Master跟多個活動的tablet server通訊,收集tablets的分配情況(4)Master掃描METADATA表得知所有的tablets集合。(4)Master如果發現有tablet并沒有分配到tablet server,它會將之放入未被分配tablets集合,這樣這個tablet就會被認為是可以被分配的。
就如我上面提到的,HBase實際上會等待所有的region server上報它負責的region情況。當然,它也會掃描.META. 表去了解有哪些region以及它們是分配到哪些region server上了。
ZooKeeper僅僅用來發布-ROOT- region所在的region server的地址。
Tablet/Region切分
在切分通知被丟失時(因為tablet server掛了或者master掛了)的情況下,當master要求tablet server裝載被切分的那個tablet時,master會發現新的tablet. Tablet server會將此次切分通知master,因為它會發現在METADATA中找到的tablet只是master要求它裝載的tablet的一部分。
Master節點會單獨利用.META.表去發現一個region去切分,但是切分事件消息被丟失的情況。Master會按往常一樣掃描.META.去發 現,哪些region并沒有被分配。一旦發現沒有被分配的region,master會用默認的策略將之分配到一個region server上。
壓緊(Compaction)
隨著寫動作的執行,內存表的大小會不斷增長。當內存表的容量到達一個臨界點時,內存表將被凍結,一個新的內存表將會被創建,被凍結的內存表將被會轉換成SSTable并被寫入到GFS中。這種minor compaction操作有2個目的:1降低tablet server的內存用量,2當一個tablet server死而復生從commit log讀取數據時,減少數據總量。當壓緊動作發生時,認可繼續執行讀寫操作。
HBase也有相應的操作,不過被命名為”flush”。對應BigTable的”minor compaction”,HBase會把最近生成的很多較小的存儲文件重寫為一個包含較多數據的大文件。
…我們將限制此類文件的數目,方式是定期地在后臺執行合并壓緊操作。合并壓緊操作就是讀取幾個SSTable和內存表的內容,然后寫到一個新的SSTable中去。一旦合并壓緊操作完成,老的SSTable和內存表就將可被丟棄。
這個動作跟HBase中的”minor compaction”差不多。
讀取所有的SSTable,重新寫成一個SSTable的合并壓緊操作被稱為是major compaction
相應的在HBase中,一個major compaction就是把所有的存儲文件(HFile)重寫成一個新的文件。
文件不可修改
要知道文件一旦被寫入將不能修改,BigTable有如下的假定:
唯一可以被修改的數據結構就是讀寫都可以的內存表(memtable)。為了避免同時讀寫內存表的沖突,我們在寫內存表的數據行的時候,會先復制一個副本,這樣讀寫就可以并行處理了。
我相信HBase中的做法跟此類似,但不是很確定。可以確定是,根據HDFS的架構,文件一旦被寫入就是不能被修改的。
我唯一的建議就是你自己去閱讀BigTable的相關資料以形成自己的見解。這篇帖子的靈感來自一個念頭:BigTable究竟實現了哪些特 性,HBase涵蓋了BigTable的哪些特性?寫這片帖子的困難毫無疑問是我們對BigTable的細節所知不多。但是關于它的論文數量–即便是 2006年的論文列表都給人留下難以磨滅的印象。HBase作為一個開源項目,而且項目的貢獻者是為數不多的有著全職工作的人,現在雖不能說跟 BigTable相提并論,但怎么說都我都認為現在的成果是一個巨大的成就。看看0.21和0.22的路線圖,將來它們之間不大的差距將會變得更加縮小。