理解HBase(一個開源的Google的BigTable實際應用)最大的困難是HBase的數據結構概念究竟是什么?首先HBase不同於一般的關系數據庫,它是一個適合於非結構化數據存儲的數據庫.另一個不同的是HBase基於列的而不是基於行的模式.
Google's BigTable論文 清楚地解釋了什么是BigTable:
Bigtable是一個疏松的分布式的持久的多維排序的map,這個map被行鍵,列鍵,和時間戳索引.每一個值都是連續的byte數組.(A Bigtable is a sparse, distributed, persistent multidimensional sorted map. The map is indexed by a row key, column key, and a timestamp; each value in the map is an uninterpreted array of bytes.)
Hadoop wiki的HBase架構 頁面提到:
HBase使用和Bigtable非常相同的數據模型.用戶存儲數據行在一個表里.一個數據行擁有一個可選擇的鍵和任意數量的列.表是疏松的存儲的,因此 用戶可以給行定義各種不同的列.(HBase uses a data model very similar to that of Bigtable. Users store data rows in labelled tables. A data row has a sortable key and an arbitrary number of columns. The table is stored sparsely, so that rows in the same table can have crazily-varying columns, if the user likes.)
一、架構思路
Hbase是基於Hadoop的項目,所以一般情況下我們使用的直接就是HDFS文件系統,這里我們不深談HDFS如何構造其分布式的文件系統,只需要知 道雖然Hbase中有多個RegionServer的概念,並不意味着數據是持久化在RegionServer上的,事實上,RegionServer是 調度者,管理Regions,但是數據是持久化在HDFS上的。明確這一點,在后面的討論中,我們直接把文件系統抽象為HDFS,不再深究。
Hbase是一個分布式的數據庫,使用Zookeeper來管理集群。在架構層面上分為Master(Zookeeper中的leader)和多個RegionServer,基本架構如圖:
在Hbase的概念中,RegionServer對應於集群中的一個節點,而一個RegionServer負責管理多個Region。一個Region代 表一張表的一部分數據,所以在Hbase中的一張表可能會需要很多個Region來存儲其數據,但是每個Region中的數據並不是雜亂無章 的,Hbase在管理Region的時候會給每個Region定義一個Rowkey的范圍,落在特定范圍內的數據將交給特定的Region,從而將負載分 攤到多個節點上,充分利用分布式的優點。另外,Hbase會自動的調節Region處在的位置,如果一個RegionServer變得Hot(大量的請求 落在這個Server管理的Region上),Hbase就會把Region移動到相對空閑的節點,依次保證集群環境被充分利用。
二、存儲模型
有了架構層面的保證,接下來的事情就只是關注於數據的具體存儲了。這里就是每個Region所承擔的工作了。我們知道一個Region代表的是一張 Hbase表中特定Rowkey范圍內的數據,而Hbase是面向列存儲的數據庫,所以在一個Region中,有多個文件來存儲這些列。Hbase中數據 列是由列簇來組織的,所以每一個列簇都會有對應的一個數據結構,Hbase將列簇的存儲數據結構抽象為Store,一個Store代表一個列簇。
所以在這里也可以看出為什么在我們查詢的時候要盡量減少不需要的列,而經常一起查詢的列要組織到一個列簇里:因為要需要查詢的列簇越多,意味着要掃描越多的Store文件,這就需要越多的時間。
我們來深入Store中存儲數據的方式。Hbase的實現是用了一種LSM 樹的結構!
LSM樹是由B+樹改進而來,所以我們首先來簡單的看看B+樹。
這是一顆簡單的B+樹,含義不言而喻,這里不多分析,但是這種數據結構並不適合Hbase中的應用場景。這樣的數據結構在內存中效率是很高的,但是 Hbase中數據是存儲在文件中的,如果按照這樣的結構來存儲,意味着我們每一次插入數據都要由一級索引找到文件再在文件中間作操作來保證數據的有序性, 這無疑是效率低下的。所以Hbase采用的是LSM樹的結構,這種結構的關鍵是,每一次的插入操作都會先進入MemStore(內存緩沖區),當 MemStore達到上限的時候,Hbase會將內存中的數據輸出為有序的StoreFile文件數據(根據Rowkey、版本、列名排序,這里已經和列 簇無關了因為Store里都屬於同一個列簇)。這樣會在Store中形成很多個小的StoreFile,當這些小的File數量達到一個閥值的時 候,Hbase會用一個線程來把這些小File合並成一個大的File。這樣,Hbase就把效率低下的文件中的插入、移動操作轉變成了單純的文件輸出、 合並操作。
由上可知,在Hbase底層的Store數據結構中,每個StoreFile內的數據是有序的,但是StoreFile之間不一定是有序的,Store只 需要管理StoreFile的索引就可以了。這里也可以看出為什么指定版本和Rowkey可以加強查詢的效率,因為指定版本和Rowkey的查詢可以利用 StoreFile的索引跳過一些肯定不包含目標數據的數據。
HBase vs Cassandra
HBase | Cassandra | |
---|---|---|
語言 | Java | Java |
出發點 | BigTable | BigTable and Dynamo |
License | Apache | Apache |
Protocol | HTTP/REST (also Thrift) | Custom, binary (Thrift) |
數據分布 | 表划分為多個region存在不同region server上 | 改進的一致性哈希(虛擬節點) |
存儲目標 | 大文件 | 小文件 |
一致性 | 強一致性 | 最終一致性,Quorum NRW策略 |
架構 | master/slave | p2p |
高可用性 | NameNode是HDFS的單點故障點 | P2P和去中心化設計,不會出現單點故障 |
伸縮性 | Region Server擴容,通過將自身發布到Master,Master均勻分布Region | 擴容需在Hash Ring上多個節點間調整數據分布 |
讀寫性能 | 數據讀寫定位可能要通過最多6次的網絡RPC,性能較低。 | 數據讀寫定位非常快 |
數據沖突處理 | 樂觀並發控制(optimistic concurrency control) | 向量時鍾 |
臨時故障處理 | Region Server宕機,重做HLog | 數據回傳機制:某節點宕機,hash到該節點的新數據自動路由到下一節點做 hinted handoff,源節點恢復后,推送回源節點。 |
永久故障恢復 | Region Server恢復,master重新給其分配region | Merkle 哈希樹,通過Gossip協議同步Merkle Tree,維護集群節點間的數據一致性 |
成員通信及錯誤檢測 | Zookeeper | 基於Gossip |
CAP | 1,強一致性,0數據丟失。2,可用性低。3,擴容方便。 | 1,弱一致性,數據可能丟失。2,可用性高。3,擴容方便。 |