HBase使用總結(jié)

原創(chuàng)文章,轉(zhuǎn)載請注明原作地址:http://m.itdecent.cn/p/0f9578df7fbc

一. 架構(gòu)


1. 數(shù)據(jù)模型

1.1 基礎(chǔ)概念
  • 表(table):列式存儲,支持高表&寬表(上億行,上百萬列)
  • 行(row):每一行由唯一的行鍵確定
  • 列族(columnFamily):每一行包含一個或多個列族,是列的集合
  • 列(column):列式存儲,列是最基本單位,可能有多個版本的值
  • 時間戳(Timestamp):列的不同版本之間用時間戳區(qū)分
  • 單元格(cell):列的每一個版本是一個單元格,是存儲的基本單位

HBase最基本的單位是列(column),一列或者多列形成一行(row),若干行數(shù)據(jù)組成了一張表(table)。聽起來是一個非常普通的列式存儲數(shù)據(jù)庫,但是,它和傳統(tǒng)數(shù)據(jù)庫有很大的不同。

1.2 與傳統(tǒng)數(shù)據(jù)庫的區(qū)別
a. HBase的每一行由唯一的行鍵確定

在某種程度上,行鍵相當(dāng)于傳統(tǒng)數(shù)據(jù)庫的primary key,區(qū)別在于,primary key是可選的,而HBase的每張表都必然會有行鍵。除了行鍵之外,HBase表不能對列添加索引。HBase是一個<key, value>形式的數(shù)據(jù)庫,行鍵就是它的key。

b. HBase引入了列族(columnFamily)的概念
  • HBase是一個列式存儲的數(shù)據(jù)庫,因此列的使用是非常靈活的,不必在表定義的時候就定好列名,但是必須在建表的時候定義好列族名字。
  • 一張HBase表存儲的列的數(shù)量可以是無限的,但是列族的數(shù)量最好控制在2-3個(原因在備注中[1]
  • 列必須屬于某個列族,不同列族之間可以有同名列
  • 列族的作用是,將那些數(shù)據(jù)量和屬性相似的列聚集在一起,以便我們給這些列定義一些共同的存儲方式屬性(e.g. 數(shù)據(jù)壓縮,保存到讀緩存中)
c. HBase的列值可以有多版本

在HBase表中,行鍵、列族、列名和時間戳才能唯一確定一個值。每個值是一個單元格(cell),是存儲的基本單位。每一行數(shù)據(jù)的每一列,都可以存儲多個值,每個版本的值之間通過時間戳確定,在存儲的時候,這些值也按照時間戳逆序排列,保證客戶端永遠(yuǎn)讀到最新的數(shù)據(jù)。但是,每個列族可以存儲的最大版本數(shù)是確定的,并且是在建表的時候就定義好的。

另外,單元格的時間戳是可以由用戶自行指定的,如果不指定,服務(wù)器就會將接收到寫請求的服務(wù)器時間作為單元格的時間戳。通常情況下,最好不要自己指定時間戳,因?yàn)榭蛻舳丝偸请y以保證,指定的時間戳是按照寫順序遞增的。

d. 反范式化

HBase是一個NoSQL(Not-only-SQL)數(shù)據(jù)庫,不提供復(fù)雜的查詢方式,包括join。另外,相對于MySQL,HBase的可擴(kuò)展性很好,存儲資源要廉價很多。因此,在設(shè)計數(shù)據(jù)庫的時候,我們總是傾向于反范式化,以方便后期的數(shù)據(jù)查詢

1.3 數(shù)據(jù)模型抽象

HBase實(shí)際上是按照谷歌的bigtable實(shí)現(xiàn)的,而谷歌在bigtable論文的開篇就介紹了bigtable的特點(diǎn):A Bigtable is a sparse, distributed, persistent multidimensional sorted map。所以HBase在本質(zhì)上,是一張有序的多維map,數(shù)據(jù)模型可以抽象成:

<rowKey : columnFamily : qualifier : timestamp, value>

這樣的優(yōu)點(diǎn)是,HBase只存儲有值的單元格,對于一張稀疏表來說,可以節(jié)省很多存儲空間;但是,為每個cell都存儲了rowKey, columnFamily, qualifier,因此cf的名字不要太長。

2. 存儲模型

存儲模型
2.1 存儲概覽
a. 數(shù)據(jù)分片存儲

在HBase中,一張表的數(shù)據(jù)會被分成幾份,每一份數(shù)據(jù)為一個region;每個region內(nèi)存儲的key是連續(xù)范圍內(nèi)的,不同region存儲的key范圍不重合;這些region可能被存儲在同一臺機(jī)器上,也可能存儲在不同的機(jī)器上。HBase作為一個分布式數(shù)據(jù)庫,對數(shù)據(jù)進(jìn)行分片,可以提升吞吐量。

b. HLog:Write-Ahead-Log,寫操作先寫日志

HLog的作用是,當(dāng)一臺regionServer crash了,可以利用HLog來恢復(fù)內(nèi)存中未持久化到硬盤中的數(shù)據(jù)。需要注意的是,同一臺server上的所有region共用一個HLog實(shí)例,因?yàn)榧偃缑總€region擁有一個獨(dú)立的HLog,服務(wù)器會花費(fèi)很多時間在磁盤尋道上。

c. MemStore:寫緩存,每個store擁有獨(dú)立的寫緩存

在HBase中,所有的寫操作全部寫到內(nèi)存中,當(dāng)寫緩存(MemStore)寫滿,再刷寫(flush)到磁盤中[2],形成一個新的文件。這樣做的目的,是為了高速響應(yīng)那些寫請求。

d. HFile:磁盤文件

在存儲上,HBase完全依賴HDFS,磁盤操作是直接調(diào)用HDFS的API(HDFS在維持data locality這一點(diǎn)上足夠智能)。另外,之前提過HBase定義列族的一個原因是為了方便存儲,事實(shí)上,同一列族的數(shù)據(jù)會被寫到同一文件,因?yàn)榇鎯μ匦员緛砭褪前凑樟凶宥x的。HBase的數(shù)據(jù)在底層文件中時以KeyValue鍵值對的形式存儲的,HBase沒有數(shù)據(jù)類型,HFile中存儲的是字節(jié),這些字節(jié)按字典序排列。

e. 讀緩存:同一server上所有region共用

既然HBase有寫緩存,相對應(yīng)的應(yīng)該有讀緩存。與寫緩存不同的是,HBase的讀緩存是同一server上的所有region共用的。當(dāng)HBase讀取磁盤上某一條數(shù)據(jù)時,HBase會將整個HFile block[3]讀到cache中[4]。因此,當(dāng)client請求臨近的數(shù)據(jù)時,因?yàn)榕R近數(shù)據(jù)已經(jīng)被緩存到內(nèi)存中,HBase的響應(yīng)會更快,也就是說,HBase鼓勵將那些相似的,會被一起查找的數(shù)據(jù)存放在一起。另外,當(dāng)我們在做全表掃描時,為了不刷走讀緩存中的熱數(shù)據(jù),千萬記得關(guān)閉讀緩存的功能。

2.2 行鍵的索引
a. 行數(shù)據(jù)查找步驟
  • hbase:meta表查找,獲取數(shù)據(jù)所在的region id
  • 根據(jù)region id,到對應(yīng)的region server上查找,在server上查找對應(yīng)記錄時,有三種方式
    (1) 掃描緩存
    (2) 塊索引
    (3) 布隆過濾器
b. rowKey索引:hbase:meta表

client會首先獲取hbase:meta表的位置,再到對應(yīng)的region server上讀取這張表的內(nèi)容(hbase:meta表其實(shí)就是一張HBase表)。讀到這張hbase:meta表之后,client會緩存這張表,這張之后的查找就可以復(fù)用了。hbase:meta表的內(nèi)容如下:

| key                                                               | value                                                                               |
| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------|
|                                                                   | info:regioninfo (serialized HRegionInfo instance for this region)                   |
| Region key of the format ([table],[region start key],[region id]) | info:server (server:port of the RegionServer containing this region)                |
|                                                                   | info:serverstartcode (start-time of the RegionServer process containing this region)|

因此,通過查找hbase:meta表,client可以得知對應(yīng)的數(shù)據(jù)存儲在哪臺server的那個region上,接下來就要到對應(yīng)的server上查找相關(guān)數(shù)據(jù)了。

c. region server上的數(shù)據(jù)查找

當(dāng)接收到一個讀請求,server會初始化一個scanner查找內(nèi)存中是否有相關(guān)數(shù)據(jù);一個scanner查找硬盤文件中是否存儲了相關(guān)數(shù)據(jù)。查找硬盤文件是一件相當(dāng)繁重的體力活,為了加快文件查找,HBase借助了兩個工具:塊索引和布隆過濾器。

塊索引和布隆過濾器
d. rowKey索引:塊索引

塊索引存儲在HFile文件中的末端,當(dāng)HBase在查找文件中是否保存了目標(biāo)數(shù)據(jù)時,首先會將塊索引讀入內(nèi)存。因?yàn)镠File中的KeyValue字節(jié)數(shù)據(jù),是按照字典序排列的,而塊索引存儲了文件中所有塊(HFile block)的起始key,因此可以根據(jù)塊索引迅速定位需要查找的塊,只將可能保存了目標(biāo)數(shù)據(jù)的塊讀到內(nèi)存中,能加快查找速度。

e. rowKey索引:布隆過濾器

雖然塊索引幫助減少了需要讀到內(nèi)存中的數(shù)據(jù),我們依然需要查找每個文件中的一個塊,才能完成磁盤數(shù)據(jù)查找,而布隆過濾器則可以幫助我們跳過那些顯然不包含目標(biāo)數(shù)據(jù)的文件。因?yàn)椴悸∵^濾器的特點(diǎn)是,能迅速判斷一個數(shù)據(jù)集合中包不包含目標(biāo)數(shù)據(jù),判斷結(jié)果有兩種,不包含和可能包含。如下圖所示,布隆過濾器能幫助跳過一些肯定不包含目標(biāo)數(shù)據(jù)的文件。

布隆過濾器

和塊索引一樣,布隆過濾器也被存儲在文件末端,會被優(yōu)先加載到內(nèi)存中。另外,布隆過濾器分行式和列式兩種,列式需要更多的存儲空間,因此如果是按行讀取數(shù)據(jù),沒必要使用列式的布隆過濾器。布隆過濾器和塊索引的對比如下:

塊索引 布隆過濾器
快速定位記錄在文件中可能的位置 快速判斷文件中是否包含相應(yīng)記錄
2.3 將隨機(jī)寫轉(zhuǎn)化成順序?qū)?/h5>

HBase的存儲是完全基于HDFS的,而HDFS的特點(diǎn)是不能對磁盤文件進(jìn)行隨機(jī)修改。因此,HBase無法對已寫入磁盤文件的表記錄進(jìn)行隨機(jī)修改,但是對于數(shù)據(jù)庫來說,支持對表記錄進(jìn)行隨機(jī)修改是基本功能。為此,HBase的方法是將隨機(jī)寫的操作轉(zhuǎn)化成順序?qū)憽?/p>

首先,隨機(jī)的寫操作轉(zhuǎn)化為文件追加操作,按照時間順序排列,client讀數(shù)據(jù)時總是優(yōu)先讀到最新的修改。而刪除操作則轉(zhuǎn)化為寫入一個tombstone標(biāo)記,標(biāo)記著早于這個tombstone時間戳的對應(yīng)行所有記錄作廢。

顯然,因?yàn)镠Base總是進(jìn)行文件追加,隨著時間積累,文件膨脹很快。major compact的一個作用就是,真正刪除所有無效的過時數(shù)據(jù)。

2.4 HBase Compaction和Region Split
a. HBase Compaction

前面已經(jīng)提到過,HBase數(shù)據(jù)寫入的時候,總是先寫入到寫緩存(MemStore)中,當(dāng)寫緩存寫滿,則flush到磁盤形成一個新的磁盤文件??梢韵胂蟮氖牵S著時間增長,磁盤上這樣的小文件會越來越多,HBase查找數(shù)據(jù)也需要越來越長的時間。為了避免這樣的問題,HBase會做compaction,合并HFile文件,減少每次查找數(shù)據(jù)的磁盤尋道時間。compaction分為major compact和minor compact兩種:

  • Minor compact:將多個小文件簡單合并成一個大文件
  • Major compact:將同一列族的所有文件合并成一個大文件,并且刪除過期無效的數(shù)據(jù)和tombstone標(biāo)記[5]
b. Region Split

client不斷向HBase寫入數(shù)據(jù),region管理的數(shù)據(jù)量不斷膨脹。當(dāng)一個region內(nèi)存儲的數(shù)據(jù)量到達(dá)閾值,則會觸發(fā)HBase的region split操作,將老的region拆分成兩個新的子region。拆分的原則是數(shù)據(jù)量對半分。為了避免region拆分導(dǎo)致的IO瞬時上升,region拆分并不會立刻將拆分重寫所有的磁盤文件文件,而是為每個子region創(chuàng)建reference文件,這些文件指向了舊的磁盤文件中對應(yīng)記錄的起始和終止位置。等到子region的compact操作被觸發(fā),在重寫文件的時候,HBase才會為每個子region生成獨(dú)立的磁盤文件。

3. 物理模型

物理模型

HBase的架構(gòu)是一個典型的master-slave模型,HBase的master節(jié)點(diǎn)叫HMaster,slave節(jié)點(diǎn)就是RegionServer。

3.1 Master的職責(zé)

處理集群相關(guān)的請求(來自client或者其他server節(jié)點(diǎn))

  • 建表或者表變更的操作
  • 打開或者關(guān)閉一個region
  • metadata元數(shù)據(jù)的管理

集群監(jiān)控(依賴Zookeeper)

  • 監(jiān)控regionServer的狀態(tài)以及負(fù)載均衡等
  • 跟蹤hbase:meta表的位置
  • 后備master節(jié)點(diǎn)需要監(jiān)控當(dāng)前master節(jié)點(diǎn)是否活躍
3.2 RegionServer的職責(zé)

集群初始化過程中

當(dāng)一個HBase集群起來之后,HMaster會在對應(yīng)的regionServer上起一個HRegionServer進(jìn)程。HRegionServer負(fù)責(zé)打開對應(yīng)的region,并創(chuàng)建對應(yīng)的HRegion實(shí)例。當(dāng)HRegion打開之后,它會為每個表的HColumnFamily創(chuàng)建一個Store實(shí)例,ColumnFamily是用戶在創(chuàng)建表時定義好的,ColumnFamily在每個region中和Store實(shí)例一一對應(yīng)。每個Store實(shí)例包含一個或者多個StoreFile實(shí)例,StoreFile是對實(shí)際存儲數(shù)據(jù)文件HFile的輕量級封裝。每個Store對應(yīng)一個MemStore。一個HRegionServer共享一個HLog實(shí)例。

集群運(yùn)行過程中

  • compaction和split是由RegionServer獨(dú)立判斷決定是否執(zhí)行的,但過程中包含一些必要的和master、ZK的通信
  • client端發(fā)起的讀寫請求,也直接由對應(yīng)RegionServer處理(master不處理)[6],流程如下:
    (1) client端向ZK請求hbase:meta表位置,取得表內(nèi)容
    (2) 查詢meta表得知數(shù)據(jù)存在哪臺RegionServer上
    (3)直接與RegionServer通信,進(jìn)行讀寫操作

注:
[1] 同一張表的column family數(shù)量不能超過2-3個。因?yàn)槟壳?,flush和compaction操作是基于region進(jìn)行的,當(dāng)一個column family觸發(fā)了MemStore flush操作,相鄰的column family都會被刷寫到磁盤,即使它們MemStore內(nèi)的數(shù)據(jù)量還很小。因此,如果同一張表內(nèi)column family的數(shù)量過多,flush和compaction將會帶來更多不必要的I/O負(fù)載(當(dāng)然這個問題可以通過,將flush和compaction改成列族之間互不影響來解決)。通常情況下,定義表的時候盡量使用單列族,除非列與列的查詢是相對獨(dú)立的,再考慮使用多個列族,比如client并不會同時請求兩個列族的數(shù)據(jù)。
當(dāng)同一張內(nèi)有多個列族時,注意一些列族間的數(shù)據(jù)量是否一致,假如列族A和列族B的數(shù)據(jù)量相差懸殊,列族A的大數(shù)據(jù)量會導(dǎo)致表數(shù)據(jù)被分片到很多個機(jī)器上,此時再對列族B的數(shù)據(jù)做掃描,效率會很低。

[2] 能觸發(fā)MemStore flush操作的有三種情形:

  • 當(dāng)一個MemStore的數(shù)據(jù)量達(dá)到hbase.hregion.memstore.flush.size,同一region的所有MemStore的數(shù)據(jù)都會被刷寫道磁盤
  • 當(dāng)全部的MemStore的數(shù)據(jù)量達(dá)到hbase.regionserver.global.memstore.upperLimit,同一RegionServer的多個region的MemStore的數(shù)據(jù)會被刷寫到磁盤。按照每region的MemStore大小,從大到小刷寫到磁盤,直到總的MemStore大小下降到hbase.regionserver.global.memstore.upperLimit
  • 當(dāng)region server的WAL的log數(shù)量達(dá)到hbase.regionserver.max.logs,該server上多個region的MemStore會被刷寫到磁盤(按照時間順序),以降低WAL的大小。

[3] HFile block:HFile塊和Hadoop塊是兩個獨(dú)立的概念。HFile塊的默認(rèn)大小是64KB,而Hadoop塊的默認(rèn)大小為64MB。另外,如果有需要的話,用戶還可以自行定義HFile塊大小。一般情況下,如果客戶端都是順序訪問表記錄,在讀緩存的作用下,建議使用較大的HFile塊;如果客戶端都是隨機(jī)訪問表記錄,建議使用較小的HFile塊,不過也需要更多的內(nèi)存來存儲塊索引(塊索引會優(yōu)先存放在cache中),并且創(chuàng)建過程也會變得更慢,因?yàn)槲覀儽仨氃诿總€數(shù)據(jù)塊結(jié)束的時候刷寫壓縮流,導(dǎo)致一個FS I/O刷寫。

[4] 關(guān)于讀緩存的更詳細(xì)資料:http://zh.hortonworks.com/blog/hbase-blockcache-101/

[5] HBase默認(rèn)每7天對HBase做一輪major compact,在0.96的版本之前,這個周期是1天。

[6] 正因?yàn)閏lient讀寫數(shù)據(jù)的過程沒有master節(jié)點(diǎn)的參與,如果master failover了,hbase集群仍然可以穩(wěn)定運(yùn)行一段時間,只是像region分裂,RegionServer failover處理等需要master節(jié)點(diǎn)參與的工作,無法完成了。

未完待續(xù)...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容