訂閱
糾錯
加入自媒體

HBase 知識體系吐血總結

2021-11-17 11:37
園陌
關注

HBase 涉及的知識點如下圖所示,本文將逐一講解:

本文目錄如上圖

本文檔參考了關于 HBase 的官網及其他眾多資料整理而成,為了整潔的排版及舒適的閱讀,對于模糊不清晰的圖片及黑白圖片進行重新繪制成了高清彩圖。

一、HBase 基礎1. HBase 基本介紹

簡介

HBase 是 BigTable 的開源 Java 版本。是建立在 HDFS 之上,提供高可靠性、高性能、列存儲、可伸縮、實時讀寫 NoSql 的數(shù)據(jù)庫系統(tǒng)。

它介于 NoSql 和 RDBMS 之間,僅能通過主鍵(row key)和主鍵的 range 來檢索數(shù)據(jù),僅支持單行事務(可通過 hive 支持來實現(xiàn)多表 join 等復雜操作)。

主要用來存儲結構化和半結構化的松散數(shù)據(jù)。

Hbase 查詢數(shù)據(jù)功能很簡單,不支持 join 等復雜操作,不支持復雜的事務(行級的事務)Hbase 中支持的數(shù)據(jù)類型:byte[]與 hadoop 一樣,Hbase 目標主要依靠橫向擴展,通過不斷增加廉價的商用服務器,來增加計算和存儲能力。

HBase 中的表一般有這樣的特點:

大:一個表可以有上十億行,上百萬列面向列:面向列(族)的存儲和權限控制,列(族)獨立檢索。稀疏:對于為空(null)的列,并不占用存儲空間,因此,表可以設計的非常稀疏。

HBase 的發(fā)展歷程

HBase 的原型是 Google 的 BigTable 論文,受到了該論文思想的啟發(fā),目前作為 Hadoop 的子項目來開發(fā)維護,用于支持結構化的數(shù)據(jù)存儲。

2006 年 Google 發(fā)表 BigTable 白皮書2006 年開始開發(fā) HBase2008 HBase 成為了 Hadoop 的子項目2010 年 HBase 成為 Apache 頂級項目2. HBase 與 Hadoop 的關系

HDFS

為分布式存儲提供文件系統(tǒng)針對存儲大尺寸的文件進行優(yōu)化,不需要對 HDFS 上的文件進行隨機讀寫直接使用文件數(shù)據(jù)模型不靈活使用文件系統(tǒng)和處理框架優(yōu)化一次寫入,多次讀取的方式

HBase

提供表狀的面向列的數(shù)據(jù)存儲針對表狀數(shù)據(jù)的隨機讀寫進行優(yōu)化使用 key-value 操作數(shù)據(jù)提供靈活的數(shù)據(jù)模型使用表狀存儲,支持 MapReduce,依賴 HDFS優(yōu)化了多次讀,以及多次寫3. RDBMS 與 HBase 的對比

關系型數(shù)據(jù)庫

結構:

數(shù)據(jù)庫以表的形式存在支持 FAT、NTFS、EXT、文件系統(tǒng)使用 Commit log 存儲日志參考系統(tǒng)是坐標系統(tǒng)使用主鍵(PK)支持分區(qū)使用行、列、單元格

功能:

支持向上擴展使用 SQL 查詢面向行,即每一行都是一個連續(xù)單元數(shù)據(jù)總量依賴于服務器配置具有 ACID 支持適合結構化數(shù)據(jù)傳統(tǒng)關系型數(shù)據(jù)庫一般都是中心化的支持事務支持 Join

HBase

結構:

數(shù)據(jù)庫以 region 的形式存在支持 HDFS 文件系統(tǒng)使用 WAL(Write-Ahead Logs)存儲日志參考系統(tǒng)是 Zookeeper使用行鍵(row key)支持分片使用行、列、列族和單元格

功能:

支持向外擴展使用 API 和 MapReduce 來訪問 HBase 表數(shù)據(jù)面向列,即每一列都是一個連續(xù)的單元數(shù)據(jù)總量不依賴具體某臺機器,而取決于機器數(shù)量HBase 不支持 ACID(Atomicity、Consistency、Isolation、Durability)適合結構化數(shù)據(jù)和非結構化數(shù)據(jù)一般都是分布式的HBase 不支持事務不支持 Join4. HBase 特征簡要海量存儲

Hbase 適合存儲 PB 級別的海量數(shù)據(jù),在 PB 級別的數(shù)據(jù)以及采用廉價 PC 存儲的情況下,能在幾十到百毫秒內返回數(shù)據(jù)。這與 Hbase 的極易擴展性息息相關。正式因為 Hbase 良好的擴展性,才為海量數(shù)據(jù)的存儲提供了便利。

列式存儲

這里的列式存儲其實說的是列族存儲,Hbase 是根據(jù)列族來存儲數(shù)據(jù)的。列族下面可以有非常多的列,列族在創(chuàng)建表的時候就必須指定。

極易擴展

Hbase 的擴展性主要體現(xiàn)在兩個方面,一個是基于上層處理能力(RegionServer)的擴展,一個是基于存儲的擴展(HDFS)。通過橫向添加 RegionSever 的機器,進行水平擴展,提升 Hbase 上層的處理能力,提升 Hbsae 服務更多 Region 的能力。備注:RegionServer 的作用是管理 region、承接業(yè)務的訪問,這個后面會詳細的介紹通過橫向添加 Datanode 的機器,進行存儲層擴容,提升 Hbase 的數(shù)據(jù)存儲能力和提升后端存儲的讀寫能力。

高并發(fā)

由于目前大部分使用 Hbase 的架構,都是采用的廉價 PC,因此單個 IO 的延遲其實并不小,一般在幾十到上百 ms 之間。這里說的高并發(fā),主要是在并發(fā)的情況下,Hbase 的單個 IO 延遲下降并不多。能獲得高并發(fā)、低延遲的服務。

稀疏

稀疏主要是針對 Hbase 列的靈活性,在列族中,你可以指定任意多的列,在列數(shù)據(jù)為空的情況下,是不會占用存儲空間的。

二、HBase 基礎架構

HMaster

功能:

監(jiān)控 RegionServer處理 RegionServer 故障轉移處理元數(shù)據(jù)的變更處理 region 的分配或移除在空閑時間進行數(shù)據(jù)的負載均衡通過 Zookeeper 發(fā)布自己的位置給客戶端RegionServer

功能:

負責存儲 HBase 的實際數(shù)據(jù)處理分配給它的 Region刷新緩存到 HDFS維護 HLog執(zhí)行壓縮負責處理 Region 分片

組件:

Write-Ahead logs

HBase 的修改記錄,當對 HBase 讀寫數(shù)據(jù)的時候,數(shù)據(jù)不是直接寫進磁盤,它會在內存中保留一段時間(時間以及數(shù)據(jù)量閾值可以設定)。但把數(shù)據(jù)保存在內存中可能有更高的概率引起數(shù)據(jù)丟失,為了解決這個問題,數(shù)據(jù)會先寫在一個叫做 Write-Ahead logfile 的文件中,然后再寫入內存中。所以在系統(tǒng)出現(xiàn)故障的時候,數(shù)據(jù)可以通過這個日志文件重建。

HFile

這是在磁盤上保存原始數(shù)據(jù)的實際的物理文件,是實際的存儲文件。

Store

HFile 存儲在 Store 中,一個 Store 對應 HBase 表中的一個列族。

MemStore

顧名思義,就是內存存儲,位于內存中,用來保存當前的數(shù)據(jù)操作,所以當數(shù)據(jù)保存在 WAL 中之后,RegsionServer 會在內存中存儲鍵值對。

Region

Hbase 表的分片,HBase 表會根據(jù) RowKey 值被切分成不同的 region 存儲在 RegionServer 中,在一個 RegionServer 中可以有多個不同的 region。

三、HBase 常用 shell 操作1) 添加操作進入 HBase 客戶端命令操作界面$ bin/hbase shell

查看幫助命令hbase(main):001:0> help

查看當前數(shù)據(jù)庫中有哪些表hbase(main):002:0> list

創(chuàng)建一張表

創(chuàng)建 user 表,包含 info、data 兩個列族

hbase(main):010:0> create 'user', 'info', 'data'

或者

hbase(main):010:0> create 'user', {NAME => 'info', VERSIONS => '3'},{NAME => 'data'}
添加數(shù)據(jù)操作

向 user 表中插入信息,row key 為 rk0001,列族 info 中添加 name 列標示符,值為 zhangsan

hbase(main):011:0> put 'user', 'rk0001', 'info:name', 'zhangsan'

向 user 表中插入信息,row key 為 rk0001,列族 info 中添加 gender 列標示符,值為 female

hbase(main):012:0> put 'user', 'rk0001', 'info:gender', 'female'

向 user 表中插入信息,row key 為 rk0001,列族 info 中添加 age 列標示符,值為 20

hbase(main):013:0> put 'user', 'rk0001', 'info:age', 20

向 user 表中插入信息,row key 為 rk0001,列族 data 中添加 pic 列標示符,值為 picture

hbase(main):014:0> put 'user', 'rk0001', 'data:pic', 'picture'

2) 查詢操作通過 rowkey 進行查詢

獲取 user 表中 row key 為 rk0001 的所有信息

hbase(main):015:0> get 'user', 'rk0001'

查看 rowkey 下面的某個列族的信息

獲取 user 表中 row key 為 rk0001,info 列族的所有信息

hbase(main):016:0> get 'user', 'rk0001', 'info'

查看 rowkey 指定列族指定字段的值

獲取 user 表中 row key 為 rk0001,info 列族的 name、age 列標示符的信息

hbase(main):017:0> get 'user', 'rk0001', 'info:name', 'info:age'

查看 rowkey 指定多個列族的信息

獲取 user 表中 row key 為 rk0001,info、data 列族的信息

hbase(main):018:0> get 'user', 'rk0001', 'info', 'data'

或者這樣寫

hbase(main):019:0> get 'user', 'rk0001', {COLUMN => ['info', 'data']}

或者這樣寫

hbase(main):020:0> get 'user', 'rk0001', {COLUMN => ['info:name', 'data:pic']}

指定 rowkey 與列值查詢

獲取 user 表中 row key 為 rk0001,cell 的值為 zhangsan 的信息

hbase(main):030:0> get 'user', 'rk0001', {FILTER => "ValueFilter(=, 'binary:zhangsan')"}

指定 rowkey 與列值模糊查詢

獲取 user 表中 row key 為 rk0001,列標示符中含有 a 的信息

hbase(main):031:0> get 'user', 'rk0001', {FILTER => "(QualifierFilter(=,'substring:a'))"}

繼續(xù)插入一批數(shù)據(jù)

hbase(main):032:0> put 'user', 'rk0002', 'info:name', 'fanbingbing'

hbase(main):033:0> put 'user', 'rk0002', 'info:gender', 'female'

hbase(main):034:0> put 'user', 'rk0002', 'info:nationality', '中國'

hbase(main):035:0> get 'user', 'rk0002', {FILTER => "ValueFilter(=, 'binary:中國')"}

查詢所有數(shù)據(jù)

查詢 user 表中的所有信息

scan 'user'

列族查詢

查詢 user 表中列族為 info 的信息

scan 'user', {COLUMNS => 'info'}

scan 'user', {COLUMNS => 'info', RAW => true, VERSIONS => 5}

scan 'user', {COLUMNS => 'info', RAW => true, VERSIONS => 3}

多列族查詢

查詢 user 表中列族為 info 和 data 的信息

scan 'user', {COLUMNS => ['info', 'data']}

scan 'user', {COLUMNS => ['info:name', 'data:pic']}

指定列族與某個列名查詢

查詢 user 表中列族為 info、列標示符為 name 的信息

scan 'user', {COLUMNS => 'info:name'}

指定列族與列名以及限定版本查詢

查詢 user 表中列族為 info、列標示符為 name 的信息,并且版本最新的 5 個

scan 'user', {COLUMNS => 'info:name', VERSIONS => 5}

指定多個列族與按照數(shù)據(jù)值模糊查詢

查詢 user 表中列族為 info 和 data 且列標示符中含有 a 字符的信息

scan 'user', {COLUMNS => ['info', 'data'], FILTER => "(QualifierFilter(=,'substring:a'))"}

rowkey 的范圍值查詢

查詢 user 表中列族為 info,rk 范圍是(rk0001, rk0003)的數(shù)據(jù)

scan 'user', {COLUMNS => 'info', STARTROW => 'rk0001', ENDROW => 'rk0003'}

指定 rowkey 模糊查詢

查詢 user 表中 row key 以 rk 字符開頭的

scan 'user',{FILTER=>"PrefixFilter('rk')"}

指定數(shù)據(jù)范圍值查詢

查詢 user 表中指定范圍的數(shù)據(jù)

scan 'user', {TIMERANGE => [1392368783980, 1392380169184]}

統(tǒng)計一張表有多少行數(shù)據(jù)count 'user'

3) 更新操作更新數(shù)據(jù)值

更新操作同插入操作一模一樣,只不過有數(shù)據(jù)就更新,沒數(shù)據(jù)就添加。

更新版本號

將 user 表的 f1 列族版本號改為 5

hbase(main):050:0> alter 'user', NAME => 'info', VERSIONS => 5

4) 刪除操作指定 rowkey 以及列名進行刪除

刪除 user 表 row key 為 rk0001,列標示符為 info:name 的數(shù)據(jù)

hbase(main):045:0> delete 'user', 'rk0001', 'info:name'

指定 rowkey,列名以及字段值進行刪除

刪除 user 表 row key 為 rk0001,列標示符為 info:name,timestamp 為 1392383705316 的數(shù)據(jù)

delete 'user', 'rk0001', 'info:name', 1392383705316

刪除一個列族

刪除一個列族

alter 'user', NAME => 'info', METHOD => 'delete'

或者

alter 'user', NAME => 'info', METHOD => 'delete'

清空表數(shù)據(jù)hbase(main):017:0> truncate 'user'

刪除表

首先需要先讓該表為 disable 狀態(tài),使用命令:

hbase(main):049:0> disable 'user

然后才能 drop 這個表,使用命令:

hbase(main):050:0> drop 'user'

注意:如果直接 drop 表,會報錯:Drop the named table. Table must first be disabled

四、HBase 的高級 shell 管理命令status

例如:顯示服務器狀態(tài)

hbase(main):058:0> status 'node01'

whoami

顯示 HBase 當前用戶,例如:

hbase> whoami

list

顯示當前所有的表

hbase> list

count

統(tǒng)計指定表的記錄數(shù),例如:

hbase> count 'user'

describe

展示表結構信息

hbase> describe 'user'

exists

檢查表是否存在,適用于表量特別多的情況

hbase> exists 'user'

is_enabled、is_disabled

檢查表是否啟用或禁用

hbase> is_enabled 'user'

alter

該命令可以改變表和列族的模式,例如:

為當前表增加列族:

hbase> alter 'user', NAME => 'CF2', VERSIONS => 2

為當前表刪除列族:

hbase(main):002:0>  alter 'user', 'delete' => 'CF2'

disable/enable

禁用一張表/啟用一張表

drop

刪除一張表,記得在刪除表之前必須先禁用

truncate

清空表

圖片標題

圖片標題

圖片標題

圖片標題

圖片標題

圖片標題

圖片標題

圖片標題

圖片標題

         

圖片標題

圖片標題

2. 過濾器查詢

過濾器的類型很多,但是可以分為兩大類——比較過濾器,專用過濾器。

過濾器的作用是在服務端判斷數(shù)據(jù)是否滿足條件,然后只將滿足條件的數(shù)據(jù)返回給客戶端;

hbase 過濾器的比較運算符:

LESS  <

LESS_OR_EQUAL <=

EQUAL =

NOT_EQUAL <>

GREATER_OR_EQUAL >=

GREATER >

NO_OP 排除所有

Hbase 過濾器的比較器(指定比較機制):

BinaryComparator  按字節(jié)索引順序比較指定字節(jié)數(shù)組,采用Bytes.compareTo(byte[])

BinaryPrefixComparator 跟前面相同,只是比較左端的數(shù)據(jù)是否相同

NullComparator 判斷給定的是否為空

BitComparator 按位比較

RegexStringComparator 提供一個正則的比較器,僅支持 EQUAL 和非EQUAL

SubstringComparator 判斷提供的子串是否出現(xiàn)在value中。

1) 比較過濾器rowKey 過濾器 RowFilter

通過 RowFilter 過濾比 rowKey 0003 小的所有值出來

@Test

public  void rowKeyFilter() throws IOException {
 

圖片標題

列族過濾器 FamilyFilter

查詢比 f2 列族小的所有的列族內的數(shù)據(jù)

@Test

public  void familyFilter() throws IOException {

圖片標題

列過濾器 QualifierFilter

只查詢 name 列的值

@Test

public  void qualifierFilter() throws IOException {

圖片標題

列值過濾器 ValueFilter

查詢所有列當中包含 8 的數(shù)據(jù)

@Test

圖片標題

2) 專用過濾器單列值過濾器 SingleColumnValueFilter

SingleColumnValueFilter 會返回滿足條件的整列值的所有字段

@Test

圖片標題

myuser.close();


列值排除過濾器 SingleColumnValueExcludeFilter

與 SingleColumnValueFilter 相反,會排除掉指定的列,其他的列全部返回

rowkey 前綴過濾器 PrefixFilter

查詢以 00 開頭的所有前綴的 rowkey

@Test

圖片標題

分頁過濾器 PageFilter

分頁過濾器 PageFilter

@Test

圖片標題

圖片標題

圖片標題

3) 多過濾器綜合查詢 FilterList

需求:使用 SingleColumnValueFilter 查詢 f1 列族,name 為劉備的數(shù)據(jù),并且同時滿足 rowkey 的前綴以 00 開頭的數(shù)據(jù)(PrefixFilter)

圖片標題

圖片標題

圖片標題

六、HBase 底層原理1. 系統(tǒng)架構

HBase系統(tǒng)架構

根據(jù)這幅圖,解釋下HBase中各個組件

1) Client包含訪問hbase的接口,Client維護著一些cache來加快對hbase的訪問,比如regione的位置信息.2) Zookeeper

HBase可以使用內置的Zookeeper,也可以使用外置的,在實際生產環(huán)境,為了保持統(tǒng)一性,一般使用外置Zookeeper。

Zookeeper在HBase中的作用:

保證任何時候,集群中只有一個master存貯所有Region的尋址入口實時監(jiān)控Region Server的狀態(tài),將Region server的上線和下線信息實時通知給Master3) HMaster為Region server分配region負責region server的負載均衡發(fā)現(xiàn)失效的region server并重新分配其上的regionHDFS上的垃圾文件回收處理schema更新請求4) HRegion ServerHRegion server維護HMaster分配給它的region,處理對這些region的IO請求HRegion server負責切分在運行過程中變得過大的region從圖中可以看到,Client訪問HBase上數(shù)據(jù)的過程并不需要HMaster參與(尋址訪問Zookeeper和HRegion server,數(shù)據(jù)讀寫訪問HRegione server)

HMaster僅僅維護者table和HRegion的元數(shù)據(jù)信息,負載很低。

2. HBase的表數(shù)據(jù)模型

HBase的表結構1) 行鍵 Row Key

與nosql數(shù)據(jù)庫一樣,row key是用來檢索記錄的主鍵。訪問hbase table中的行,只有三種方式:

通過單個row key訪問通過row key的range全表掃描

Row Key 行鍵可以是任意字符串(最大長度是 64KB,實際應用中長度一般為 10-100bytes),在hbase內部,row key保存為字節(jié)數(shù)組。

Hbase會對表中的數(shù)據(jù)按照rowkey排序(字典順序)

存儲時,數(shù)據(jù)按照Row key的字典序(byte order)排序存儲。設計key時,要充分排序存儲這個特性,將經常一起讀取的行存儲放到一起。(位置相關性)。

注意:字典序對int排序的結果是1,10,100,11,12,13,14,15,16,17,18,19,2,20,21 ... 。要保持整形的自然序,行鍵必須用0作左填充。

行的一次讀寫是原子操作 (不論一次讀寫多少列)。這個設計決策能夠使用戶很容易的理解程序在對同一個行進行并發(fā)更新操作時的行為。

2) 列族 Column Family

HBase表中的每個列,都歸屬于某個列族。列族是表的schema的一部分(而列不是),必須在使用表之前定義。

列名都以列族作為前綴。例如 courses:history ,  courses:math 都屬于 courses 這個列族。

訪問控制、磁盤和內存的使用統(tǒng)計都是在列族層面進行的。列族越多,在取一行數(shù)據(jù)時所要參與IO、搜尋的文件就越多,所以,如果沒有必要,不要設置太多的列族。

3) 列 Column

列族下面的具體列,屬于某一個ColumnFamily,類似于在mysql當中創(chuàng)建的具體的列。

4) 時間戳 Timestamp

HBase中通過row和columns確定的為一個存貯單元稱為cell。每個 cell都保存著同一份數(shù)據(jù)的多個版本。版本通過時間戳來索引。時間戳的類型是 64位整型。時間戳可以由hbase(在數(shù)據(jù)寫入時自動 )賦值,此時時間戳是精確到毫秒的當前系統(tǒng)時間。時間戳也可以由客戶顯式賦值。如果應用程序要避免數(shù)據(jù)版本沖突,就必須自己生成具有唯一性的時間戳。每個 cell中,不同版本的數(shù)據(jù)按照時間倒序排序,即最新的數(shù)據(jù)排在最前面。

為了避免數(shù)據(jù)存在過多版本造成的的管理 (包括存貯和索引)負擔,hbase提供了兩種數(shù)據(jù)版本回收方式:

保存數(shù)據(jù)的最后n個版本保存最近一段時間內的版本(設置數(shù)據(jù)的生命周期TTL)。

用戶可以針對每個列族進行設置。

5) 單元 Cell

由{row key, column( =<family> + <label>), version} 唯一確定的單元。cell中的數(shù)據(jù)是沒有類型的,全部是字節(jié)碼形式存貯。

6) 版本號 VersionNum

數(shù)據(jù)的版本號,每條數(shù)據(jù)可以有多個版本號,默認值為系統(tǒng)時間戳,類型為Long。

3. 物理存儲1) 整體結構

HBase 整體結構

Table 中的所有行都按照 Row Key 的字典序排列。

Table 在行的方向上分割為多個 HRegion。

HRegion按大小分割的(默認10G),每個表一開始只有一 個HRegion,隨著數(shù)據(jù)不斷插入表,HRegion不斷增大,當增大到一個閥值的時候,HRegion就會等分會兩個新的HRegion。當Table 中的行不斷增多,就會有越來越多的 HRegion。

HRegion 是 HBase 中分布式存儲和負載均衡的最小單元。最小單元就表示不同的 HRegion 可以分布在不同的 HRegion Server 上。但一個 HRegion 是不會拆分到多個 Server 上的。

HRegion 雖然是負載均衡的最小單元,但并不是物理存儲的最小單元。事實上,HRegion 由一個或者多個 Store 組成,每個 Store 保存一個 Column Family。每個 Strore 又由一個 MemStore 和0至多個 StoreFile 組成。如上圖。

2) StoreFile 和 HFile 結構

StoreFile以HFile格式保存在HDFS上。

HFile的格式為:

HFile 格式

首先HFile文件是不定長的,長度固定的只有其中的兩塊:Trailer和FileInfo。正如圖中所示的,Trailer中有指針指向其他數(shù) 據(jù)塊的起始點。

File Info中記錄了文件的一些Meta信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等。

Data Index和Meta Index塊記錄了每個Data塊和Meta塊的起始點。

Data Block是HBase I/O的基本單元,為了提高效率,HRegionServer中有基于LRU的Block Cache機制。每個Data塊的大小可以在創(chuàng)建一個Table的時候通過參數(shù)指定,大號的Block有利于順序Scan,小號Block利于隨機查詢。每個Data塊除了開頭的Magic以外就是一個個KeyValue對拼接而成, Magic內容就是一些隨機數(shù)字,目的是防止數(shù)據(jù)損壞。

HFile里面的每個KeyValue對就是一個簡單的byte數(shù)組。但是這個byte數(shù)組里面包含了很多項,并且有固定的結構。我們來看看里面的具體結構:

HFile 具體結構

開始是兩個固定長度的數(shù)值,分別表示Key的長度和Value的長度。緊接著是Key,開始是固定長度的數(shù)值,表示RowKey的長度,緊接著是 RowKey,然后是固定長度的數(shù)值,表示Family的長度,然后是Family,接著是Qualifier,然后是兩個固定長度的數(shù)值,表示Time Stamp和Key Type(Put/Delete)。Value部分沒有這么復雜的結構,就是純粹的二進制數(shù)據(jù)了。

HFile分為六個部分:

Data Block 段–保存表中的數(shù)據(jù),這部分可以被壓縮.

Meta Block 段 (可選的)–保存用戶自定義的kv對,可以被壓縮。

File Info 段–Hfile的元信息,不被壓縮,用戶也可以在這一部分添加自己的元信息。

Data Block Index 段–Data Block的索引。每條索引的key是被索引的block的第一條記錄的key。

Meta Block Index段 (可選的)–Meta Block的索引。

Trailer–這一段是定長的。保存了每一段的偏移量,讀取一個HFile時,會首先讀取Trailer,Trailer保存了每個段的起始位置(段的Magic Number用來做安全check),然后,DataBlock Index會被讀取到內存中,這樣,當檢索某個key時,不需要掃描整個HFile,而只需從內存中找到key所在的block,通過一次磁盤io將整個 block讀取到內存中,再找到需要的key。DataBlock Index采用LRU機制淘汰。

HFile的Data Block,Meta Block通常采用壓縮方式存儲,壓縮之后可以大大減少網絡IO和磁盤IO,隨之而來的開銷當然是需要花費cpu進行壓縮和解壓縮。目前HFile的壓縮支持兩種方式:Gzip,Lzo。

3) Memstore與StoreFile

一個 HRegion 由多個 Store 組成,每個 Store 包含一個列族的所有數(shù)據(jù)Store 包括位于內存的 Memstore 和位于硬盤的 StoreFile。

寫操作先寫入 Memstore,當 Memstore 中的數(shù)據(jù)量達到某個閾值,HRegionServer 啟動 FlashCache 進程寫入 StoreFile,每次寫入形成單獨一個 StoreFile

當 StoreFile 大小超過一定閾值后,會把當前的 HRegion 分割成兩個,并由 HMaster 分配給相應的 HRegion 服務器,實現(xiàn)負載均衡

客戶端檢索數(shù)據(jù)時,先在memstore找,找不到再找storefile。

4) HLog(WAL log)

WAL 意為Write ahead log,類似 mysql 中的 binlog,用來 做災難恢復時用,Hlog記錄數(shù)據(jù)的所有變更,一旦數(shù)據(jù)修改,就可以從log中進行恢復。

每個Region Server維護一個Hlog,而不是每個Region一個。這樣不同region(來自不同table)的日志會混在一起,這樣做的目的是不斷追加單個文件相對于同時寫多個文件而言,可以減少磁盤尋址次數(shù),因此可以提高對table的寫性能。帶來的麻煩是,如果一臺region server下線,為了恢復其上的region,需要將region server上的log進行拆分,然后分發(fā)到其它region server上進行恢復。

HLog文件就是一個普通的Hadoop Sequence File:

HLog Sequence File 的Key是HLogKey對象,HLogKey中記錄了寫入數(shù)據(jù)的歸屬信息,除了table和region名字外,同時還包括 sequence number和timestamp,timestamp是”寫入時間”,sequence number的起始值為0,或者是最近一次存入文件系統(tǒng)中sequence number。HLog Sequece File的Value是HBase的KeyValue對象,即對應HFile中的KeyValue,可參見上文描述。4. 讀寫過程

1) 讀請求過程:

HRegionServer保存著meta表以及表數(shù)據(jù),要訪問表數(shù)據(jù),首先Client先去訪問zookeeper,從zookeeper里面獲取meta表所在的位置信息,即找到這個meta表在哪個HRegionServer上保存著。

接著Client通過剛才獲取到的HRegionServer的IP來訪問Meta表所在的HRegionServer,從而讀取到Meta,進而獲取到Meta表中存放的元數(shù)據(jù)。

Client通過元數(shù)據(jù)中存儲的信息,訪問對應的HRegionServer,然后掃描所在HRegionServer的Memstore和Storefile來查詢數(shù)據(jù)。

最后HRegionServer把查詢到的數(shù)據(jù)響應給Client。

查看meta表信息

hbase(main):011:0> scan 'hbase:meta'

2) 寫請求過程:

Client也是先訪問zookeeper,找到Meta表,并獲取Meta表元數(shù)據(jù)。

確定當前將要寫入的數(shù)據(jù)所對應的HRegion和HRegionServer服務器。

Client向該HRegionServer服務器發(fā)起寫入數(shù)據(jù)請求,然后HRegionServer收到請求并響應。

Client先把數(shù)據(jù)寫入到HLog,以防止數(shù)據(jù)丟失。

然后將數(shù)據(jù)寫入到Memstore。

如果HLog和Memstore均寫入成功,則這條數(shù)據(jù)寫入成功

如果Memstore達到閾值,會把Memstore中的數(shù)據(jù)flush到Storefile中。

當Storefile越來越多,會觸發(fā)Compact合并操作,把過多的Storefile合并成一個大的Storefile。

當Storefile越來越大,Region也會越來越大,達到閾值后,會觸發(fā)Split操作,將Region一分為二。

細節(jié)描述:

HBase使用MemStore和StoreFile存儲對表的更新。數(shù)據(jù)在更新時首先寫入Log(WAL log)和內存(MemStore)中,MemStore中的數(shù)據(jù)是排序的,當MemStore累計到一定閾值時,就會創(chuàng)建一個新的MemStore,并且將老的MemStore添加到flush隊列,由單獨的線程flush到磁盤上,成為一個StoreFile。于此同時,系統(tǒng)會在zookeeper中記錄一個redo point,表示這個時刻之前的變更已經持久化了。當系統(tǒng)出現(xiàn)意外時,可能導致內存(MemStore)中的數(shù)據(jù)丟失,此時使用Log(WAL log)來恢復checkpoint之后的數(shù)據(jù)。

StoreFile是只讀的,一旦創(chuàng)建后就不可以再修改。因此HBase的更新其實是不斷追加的操作。當一個Store中的StoreFile達到一定的閾值后,就會進行一次合并(minor_compact, major_compact),將對同一個key的修改合并到一起,形成一個大的StoreFile,當StoreFile的大小達到一定閾值后,又會對 StoreFile進行split,等分為兩個StoreFile。

由于對表的更新是不斷追加的,compact時,需要訪問Store中全部的 StoreFile和MemStore,將他們按row key進行合并,由于StoreFile和MemStore都是經過排序的,并且StoreFile帶有內存中索引,合并的過程還是比較快。

5. HRegion管理1) HRegion分配

任何時刻,一個HRegion只能分配給一個HRegion Server。HMaster記錄了當前有哪些可用的HRegion Server。以及當前哪些HRegion分配給了哪些HRegion Server,哪些HRegion還沒有分配。當需要分配的新的HRegion,并且有一個HRegion Server上有可用空間時,HMaster就給這個HRegion Server發(fā)送一個裝載請求,把HRegion分配給這個HRegion Server。HRegion Server得到請求后,就開始對此HRegion提供服務。

2) HRegion Server上線

HMaster使用zookeeper來跟蹤HRegion Server狀態(tài)。當某個HRegion Server啟動時,會首先在zookeeper上的server目錄下建立代表自己的znode。由于HMaster訂閱了server目錄上的變更消息,當server目錄下的文件出現(xiàn)新增或刪除操作時,HMaster可以得到來自zookeeper的實時通知。因此一旦HRegion Server上線,HMaster能馬上得到消息。

3) HRegion Server下線

當HRegion Server下線時,它和zookeeper的會話斷開,zookeeper而自動釋放代表這臺server的文件上的獨占鎖。HMaster就可以確定:

HRegion Server和zookeeper之間的網絡斷開了。HRegion Server掛了。

無論哪種情況,HRegion Server都無法繼續(xù)為它的HRegion提供服務了,此時HMaster會刪除server目錄下代表這臺HRegion Server的znode數(shù)據(jù),并將這臺HRegion Server的HRegion分配給其它還活著的節(jié)點。

6. HMaster工作機制1) master上

master啟動進行以下步驟:

從zookeeper上獲取唯一一個代表active master的鎖,用來阻止其它HMaster成為master。掃描zookeeper上的server父節(jié)點,獲得當前可用的HRegion Server列表。和每個HRegion Server通信,獲得當前已分配的HRegion和HRegion Server的對應關系。掃描.META.region的集合,計算得到當前還未分配的HRegion,將他們放入待分配HRegion列表。2) master下線

由于HMaster只維護表和region的元數(shù)據(jù),而不參與表數(shù)據(jù)IO的過程,HMaster下線僅導致所有元數(shù)據(jù)的修改被凍結(無法創(chuàng)建刪除表,無法修改表的schema,無法進行HRegion的負載均衡,無法處理HRegion 上下線,無法進行HRegion的合并,唯一例外的是HRegion的split可以正常進行,因為只有HRegion Server參與),表的數(shù)據(jù)讀寫還可以正常進行。因此HMaster下線短時間內對整個HBase集群沒有影響。

從上線過程可以看到,HMaster保存的信息全是可以冗余信息(都可以從系統(tǒng)其它地方收集到或者計算出來)

因此,一般HBase集群中總是有一個HMaster在提供服務,還有一個以上的‘HMaster’在等待時機搶占它的位置。

7. HBase三個重要機制1) flush機制

1.(hbase.regionserver.global.memstore.size)默認;堆大小的40%regionServer的全局memstore的大小,超過該大小會觸發(fā)flush到磁盤的操作,默認是堆大小的40%,而且regionserver級別的flush會阻塞客戶端讀寫

2.(hbase.hregion.memstore.flush.size)默認:128M單個region里memstore的緩存大小,超過那么整個HRegion就會flush,

3.(hbase.regionserver.optionalcacheflushinterval)默認:1h內存中的文件在自動刷新之前能夠存活的最長時間

4.(hbase.regionserver.global.memstore.size.lower.limit)默認:堆大小 * 0.4 * 0.95有時候集群的“寫負載”非常高,寫入量一直超過flush的量,這時,我們就希望memstore不要超過一定的安全設置。在這種情況下,寫操作就要被阻塞一直到memstore恢復到一個“可管理”的大小, 這個大小就是默認值是堆大小 * 0.4 * 0.95,也就是當regionserver級別的flush操作發(fā)送后,會阻塞客戶端寫,一直阻塞到整個regionserver級別的memstore的大小為 堆大小 * 0.4 *0.95為止

5.(hbase.hregion.preclose.flush.size)默認為:5M當一個 region 中的 memstore 的大小大于這個值的時候,我們又觸發(fā)了region的 close時,會先運行“pre-flush”操作,清理這個需要關閉的memstore,然后 將這個 region 下線。當一個 region 下線了,我們無法再進行任何寫操作。如果一個 memstore 很大的時候,flush  操作會消耗很多時間。"pre-flush" 操作意味著在 region 下線之前,會先把 memstore 清空。這樣在最終執(zhí)行 close 操作的時候,flush 操作會很快。

6.(hbase.hstore.compactionThreshold)默認:超過3個一個store里面允許存的hfile的個數(shù),超過這個個數(shù)會被寫到新的一個hfile里面 也即是每個region的每個列族對應的memstore在flush為hfile的時候,默認情況下當超過3個hfile的時候就會對這些文件進行合并重寫為一個新文件,設置個數(shù)越大可以減少觸發(fā)合并的時間,但是每次合并的時間就會越長

2) compact機制

把小的storeFile文件合并成大的HFile文件。清理過期的數(shù)據(jù),包括刪除的數(shù)據(jù)將數(shù)據(jù)的版本號保存為1個。

3) split機制

當HRegion達到閾值,會把過大的HRegion一分為二。默認一個HFile達到10Gb的時候就會進行切分。

七、HBase 與 MapReduce 的集成

HBase 當中的數(shù)據(jù)最終都是存儲在 HDFS 上面的,HBase 天生的支持 MR 的操作,我們可以通過 MR 直接處理 HBase 當中的數(shù)據(jù),并且 MR 可以將處理后的結果直接存儲到 HBase 當中去。

需求:讀取 HBase 當中一張表的數(shù)據(jù),然后將數(shù)據(jù)寫入到 HBase 當中的另外一張表當中去。

注意:我們可以使用 TableMapper 與 TableReducer 來實現(xiàn)從 HBase 當中讀取與寫入數(shù)據(jù)。

這里我們將 myuser 這張表當中 f1 列族的 name 和 age 字段寫入到 myuser2 這張表的 f1 列族當中去。

需求一:讀取 myuser 這張表當中的數(shù)據(jù)寫入到 HBase 的另外一張表當中去:

第一步:創(chuàng)建 myuser2 這張表

注意:列族的名字要與 myuser 表的列族名字相同

hbase(main):010:0> create 'myuser2','f1'

第二步:開發(fā) MR 的程序

圖片標題

圖片標題

圖片標題

圖片標題

第三步:打包運行

將我們打好的 jar 包放到服務器上執(zhí)行:

yarn jar hbaseStudy-1.0-SNAPSHOT.jar  cn.yuan_more.hbasemr.HBaseMR

需求二:讀取 HDFS 文件,寫入到 HBase 表當中去

第一步:準備數(shù)據(jù)文件

準備數(shù)據(jù)文件,并將數(shù)據(jù)文件上傳到 HDFS 上面去。

第二步:開發(fā) MR 程序

圖片標題

     

圖片標題

需求四:通過 bulkload 的方式批量加載數(shù)據(jù)到 HBase 當中去

加載數(shù)據(jù)到 HBase 當中去的方式多種多樣,我們可以使用 HBase 的 javaAPI 或者使用 sqoop 將我們的數(shù)據(jù)寫入或者導入到 HBase 當中去,但是這些方式不是慢就是在導入的過程的占用 Region 資料導致效率低下,我們也可以通過 MR 的程序,將我們的數(shù)據(jù)直接轉換成 HBase 的最終存儲格式 HFile,然后直接 load 數(shù)據(jù)到 HBase 當中去即可。

HBase 中每張 Table 在根目錄(/HBase)下用一個文件夾存儲,Table 名為文件夾名,在 Table 文件夾下每個 Region 同樣用一個文件夾存儲,每個 Region 文件夾下的每個列族也用文件夾存儲,而每個列族下存儲的就是一些 HFile 文件,HFile 就是 HBase 數(shù)據(jù)在 HFDS 下存儲格式,所以 HBase 存儲文件最終在 hdfs 上面的表現(xiàn)形式就是 HFile,如果我們可以直接將數(shù)據(jù)轉換為 HFile 的格式,那么我們的 HBase 就可以直接讀取加載 HFile 格式的文件,就可以直接讀取了。

優(yōu)點:

導入過程不占用 Region 資源

能快速導入海量的數(shù)據(jù)

節(jié)省內存

第一步:定義 mapper 類

圖片標題

圖片標題

第三步:將代碼打成 jar 包然后運行

yarn jar original-h(huán)baseStudy-1.0-SNAPSHOT.jar  cn.yuan_more.hbasemr.HBaseLoad

第四步:開發(fā)代碼,加載數(shù)據(jù)

將輸出路徑下面的 HFile 文件,加載到 hbase 表當中去

public class LoadData {
   public static void main(String[] args) throws Exception {
       Configuration configuration = HBaseConfiguration.create();
       configuration.set("hbase.zookeeper.property.clientPort", "2181");
       configuration.set("hbase.zookeeper.quorum", "node01,node02,node03");
       Connection connection =  ConnectionFactory.createConnection(configuration);
       Admin admin = connection.getAdmin();
       Table table = connection.getTable(TableName.valueOf("myuser2"));
       LoadIncrementalHFiles load = new LoadIncrementalHFiles(configuration);
       load.doBulkLoad(new Path("hdfs://node01:8020/hbase/output_hfile"), admin,table,connection.getRegionLocator(TableName.valueOf("myuser2")));
   }

或者我們也可以通過命令行來進行加載數(shù)據(jù)。

先將 hbase 的 jar 包添加到 hadoop 的 classpath 路徑下

export HADOOP_CLASSPATH=`${HBASE_HOME}/bin/hbase mapredcp`

然后執(zhí)行以下命令,將 hbase 的 HFile 直接導入到表 myuser2 當中來

yarn jar /servers/hbase/lib/hbase-server-1.2.0.jar completebulkload /hbase/output_hfile myuser2
八、HBase 的預分區(qū)1. 為何要預分區(qū)?增加數(shù)據(jù)讀寫效率負載均衡,防止數(shù)據(jù)傾斜方便集群容災調度 region優(yōu)化 Map 數(shù)量2. 如何預分區(qū)?

每一個 region 維護著 startRow 與 endRowKey,如果加入的數(shù)據(jù)符合某個 region 維護的 rowKey 范圍,則該數(shù)據(jù)交給這個 region 維護。

3. 如何設定預分區(qū)?1) 手動指定預分區(qū)hbase(main):001:0> create 'staff','info','partition1',SPLITS => ['1000','2000','3000','4000']

完成后如圖:

2) 使用 16 進制算法生成預分區(qū)hbase(main):003:0> create 'staff2','info','partition2',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}

完成后如圖:

3) 分區(qū)規(guī)則創(chuàng)建于文件中

創(chuàng)建 splits.txt 文件內容如下:

vim splits.txt

aaaa

bbbb

cccc

dddd

然后執(zhí)行:

然后執(zhí)行:
hbase(main):004:0> create 'staff3','partition2',SPLITS_FILE => '/export/servers/splits.txt'

完成后如圖:

4) 使用 JavaAPI 創(chuàng)建預分區(qū)

代碼如下:

@Test
   public void hbaseSplit() throws IOException {
       //獲取連接
       Configuration configuration = HBaseConfiguration.create();
       configuration.set("hbase.zookeeper.quorum", "node01:2181,node02:2181,node03:2181");
       Connection connection = ConnectionFactory.createConnection(configuration);
       Admin admin = connection.getAdmin();
       //自定義算法,產生一系列Hash散列值存儲在二維數(shù)組中
       byte[][] splitKeys = {{1,2,3,4,5},{'a','b','c','d','e'}};
       //通過HTableDescriptor來實現(xiàn)我們表的參數(shù)設置,包括表名,列族等等
       HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf("stuff4"));
       //添加列族
       hTableDescriptor.addFamily(new HColumnDescriptor("f1"));
       //添加列族
       hTableDescriptor.addFamily(new HColumnDescriptor("f2"));
       admin.createTable(hTableDescriptor,splitKeys);
       admin.close();
   }
九、HBase 的 rowKey 設計技巧

HBase 是三維有序存儲的,通過 rowkey(行鍵),column key(column family 和 qualifier)和 TimeStamp(時間戳)這個三個維度可以對 HBase 中的數(shù)據(jù)進行快速定位。

HBase 中 rowkey 可以唯一標識一行記錄,在 HBase 查詢的時候,有以下幾種方式:

通過 get 方式,指定 rowkey 獲取唯一一條記錄;通過 scan 方式,設置 startRow 和 stopRow 參數(shù)進行范圍匹配;全表掃描,即直接掃描整張表中所有行記錄。1. rowkey 長度原則

rowkey 是一個二進制碼流,可以是任意字符串,最大長度 64kb,實際應用中一般為 10-100bytes,以 byte[]形式保存,一般設計成定長。

建議越短越好,不要超過 16 個字節(jié),原因如下:

數(shù)據(jù)的持久化文件 HFile 中是按照 KeyValue 存儲的,如果 rowkey 過長,比如超過 100 字節(jié),1000w 行數(shù)據(jù),光 rowkey 就要占用 100*1000w=10 億個字節(jié),將近 1G 數(shù)據(jù),這樣會極大影響 HFile 的存儲效率;

MemStore 將緩存部分數(shù)據(jù)到內存,如果 rowkey 字段過長,內存的有效利用率就會降低,系統(tǒng)不能緩存更多的數(shù)據(jù),這樣會降低檢索效率。

2. rowkey 散列原則

如果 rowkey 按照時間戳的方式遞增,不要將時間放在二進制碼的前面,建議將 rowkey 的高位作為散列字段,由程序隨機生成,低位放時間字段,這樣將提高數(shù)據(jù)均衡分布在每個 RegionServer,以實現(xiàn)負載均衡的幾率。

如果沒有散列字段,首字段直接是時間信息,所有的數(shù)據(jù)都會集中在一個 RegionServer 上,這樣在數(shù)據(jù)檢索的時候負載會集中在個別的 RegionServer 上,造成熱點問題,會降低查詢效率。

3. rowkey 唯一原則

必須在設計上保證其唯一性,rowkey 是按照字典順序排序存儲的,因此,設計 rowkey 的時候,要充分利用這個排序的特點,將經常讀取的數(shù)據(jù)存儲到一塊,將最近可能會被訪問的數(shù)據(jù)放到一塊。

4. 什么是熱點

HBase 中的行是按照 rowkey 的字典順序排序的,這種設計優(yōu)化了 scan 操作,可以將相關的行以及會被一起讀取的行存取在臨近位置,便于 scan。然而糟糕的 rowkey 設計是熱點的源頭。

熱點發(fā)生在大量的 client 直接訪問集群的一個或極少數(shù)個節(jié)點(訪問可能是讀,寫或者其他操作)。大量訪問會使熱點 region 所在的單個機器超出自身承受能力,引起性能下降甚至 region 不可用,這也會影響同一個 RegionServer 上的其他 region,由于主機無法服務其他 region 的請求。

設計良好的數(shù)據(jù)訪問模式以使集群被充分,均衡的利用。為了避免寫熱點,設計 rowkey 使得不同行在同一個 region,但是在更多數(shù)據(jù)情況下,數(shù)據(jù)應該被寫入集群的多個 region,而不是一個。

下面是一些常見的避免熱點的方法以及它們的優(yōu)缺點:

1) 加鹽

這里所說的加鹽不是密碼學中的加鹽,而是在 rowkey 的前面增加隨機數(shù),具體就是給 rowkey 分配一個隨機前綴以使得它和之前的 rowkey 的開頭不同。分配的前綴種類數(shù)量應該和你想使用數(shù)據(jù)分散到不同的 region 的數(shù)量一致。加鹽之后的 rowkey 就會根據(jù)隨機生成的前綴分散到各個 region 上,以避免熱點。

2) 哈希

哈希會使同一行永遠用一個前綴加鹽。哈希也可以使負載分散到整個集群,但是讀卻是可以預測的。使用確定的哈?梢宰尶蛻舳酥貥嬐暾 rowkey,可以使用 get 操作準確獲取某一個行數(shù)據(jù)。

3) 反轉

第三種防止熱點的方法時反轉固定長度或者數(shù)字格式的 rowkey。這樣可以使得 rowkey 中經常改變的部分(最沒有意義的部分)放在前面。這樣可以有效的隨機 rowkey,但是犧牲了 rowkey 的有序性。

反轉 rowkey 的例子以手機號為 rowkey,可以將手機號反轉后的字符串作為 rowkey,這樣的就避免了以手機號那樣比較固定開頭導致熱點問題。

3) 時間戳反轉

一個常見的數(shù)據(jù)處理問題是快速獲取數(shù)據(jù)的最近版本,使用反轉的時間戳作為 rowkey 的一部分對這個問題十分有用,可以用  Long.Max_Value - timestamp  追加到 key 的末尾,例如  [key][reverse_timestamp] , [key]  的最新值可以通過 scan [key]獲得[key]的第一條記錄,因為 HBase 中 rowkey 是有序的,第一條記錄是最后錄入的數(shù)據(jù)。

其他一些建議:

盡量減少行鍵和列族的大小在 HBase 中,value 永遠和它的 key 一起傳輸?shù)。當具體的值在系統(tǒng)間傳輸時,它的 rowkey,列名,時間戳也會一起傳輸。如果你的 rowkey 和列名很大,這個時候它們將會占用大量的存儲空間。

列族盡可能越短越好,最好是一個字符。

冗長的屬性名雖然可讀性好,但是更短的屬性名存儲在 HBase 中會更好。

十、HBase 的協(xié)處理器

http://hbase.apache.org/book.html#cp

1. 起源

Hbase 作為列族數(shù)據(jù)庫最經常被人詬病的特性包括:無法輕易建立“二級索引”,難以執(zhí)行求和、計數(shù)、排序等操作。

比如,在舊版本的(<0.92)Hbase 中,統(tǒng)計數(shù)據(jù)表的總行數(shù),需要使用 Counter 方法,執(zhí)行一次 MapReduce Job 才能得到。

雖然 HBase 在數(shù)據(jù)存儲層中集成了 MapReduce,能夠有效用于數(shù)據(jù)表的分布式計算。然而在很多情況下,做一些簡單的相加或者聚合計算的時候, 如果直接將計算過程放置在 server 端,能夠減少通訊開銷,從而獲得很好的性能提升。于是, HBase 在 0.92 之后引入了協(xié)處理器(coprocessors),實現(xiàn)一些激動人心的新特性:能夠輕易建立二次索引、復雜過濾器(謂詞下推)以及訪問控制等。

2. 協(xié)處理器有兩種:observer 和 endpoint1) observer 協(xié)處理器

Observer 類似于傳統(tǒng)數(shù)據(jù)庫中的觸發(fā)器,當發(fā)生某些事件的時候這類協(xié)處理器會被 Server 端調用。

Observer Coprocessor 就是一些散布在 HBase Server 端代碼中的 hook 鉤子,在固定的事件發(fā)生時被調用。

比如:put 操作之前有鉤子函數(shù) prePut,該函數(shù)在 put 操作執(zhí)行前會被 Region Server 調用;在 put 操作之后則有 postPut 鉤子函數(shù)。

以 HBase0.92 版本為例,它提供了三種觀察者接口:

RegionObserver:提供客戶端的數(shù)據(jù)操縱事件鉤子:Get、 Put、 Delete、 Scan 等。WALObserver:提供 WAL 相關操作鉤子。MasterObserver:提供 DDL-類型的操作鉤子。如創(chuàng)建、刪除、修改數(shù)據(jù)表等。

到 0.96 版本又新增一個 RegionServerObserver

下圖是以 RegionObserver 為例子講解 Observer 這種協(xié)處理器的原理:

2) endpoint 協(xié)處理器

Endpoint 協(xié)處理器類似傳統(tǒng)數(shù)據(jù)庫中的存儲過程,客戶端可以調用這些 Endpoint 協(xié)處理器執(zhí)行一段 Server 端代碼,并將 Server 端代碼的結果返回給客戶端進一步處理,最常見的用法就是進行聚集操作。

如果沒有協(xié)處理器,當用戶需要找出一張表中的最大數(shù)據(jù),即 max 聚合操作,就必須進行全表掃描,在客戶端代碼內遍歷掃描結果,并執(zhí)行求最大值的操作。這樣的方法無法利用底層集群的并發(fā)能力,而將所有計算都集中到 Client 端統(tǒng)一執(zhí)行,勢必效率低下。

利用 Coprocessor,用戶可以將求最大值的代碼部署到 HBase Server 端,HBase 將利用底層 cluster 的多個節(jié)點并發(fā)執(zhí)行求最大值的操作。即在每個 Region 范圍內 執(zhí)行求最大值的代碼,將每個 Region 的最大值在 Region Server 端計算出,僅僅將該 max 值返回給客戶端。在客戶端進一步將多個 Region 的最大值進一步處理而找到其中的最大值。這樣整體的執(zhí)行效率就會提高很多。

下圖是 EndPoint 的工作原理:

3. 協(xié)處理器加載方式

協(xié)處理器的加載方式有兩種,我們稱之為靜態(tài)加載方式( Static Load)和動態(tài)加載方式( Dynamic Load)。

靜態(tài)加載的協(xié)處理器稱之為 System Coprocessor

動態(tài)加載的協(xié)處理器稱之為 Table Coprocessor。

1) 靜態(tài)加載

通過修改 hbase-site.xml 這個文件來實現(xiàn), 啟動全局 aggregation,能過操縱所有的表上的數(shù)據(jù)。只需要添加如下代碼:

<property>

<name>hbase.coprocessor.user.region.classes</name>

<value>org.apache.hadoop.hbase.coprocessor.AggregateImplementation</value>

</property>

2) 動態(tài)加載

啟用表 aggregation,只對特定的表生效。通過 HBase Shell 來實現(xiàn)。

disable 指定表

hbase> disable 'mytable'

添加 aggregation

hbase> alter 'mytable', METHOD => 'table_att','coprocessor'=>
'|org.apache.Hadoop.hbase.coprocessor.AggregateImplementation||'

重啟指定表

hbase> enable 'mytable'

協(xié)處理器卸載

disable 'mytable'

alter 'mytable', METHOD => 'table_att_unset',NAME=>'coprocessor$1'

enable 'test'

十一、HBase當中的二級索引的簡要介紹

由于HBase的查詢比較弱,如果需要實現(xiàn)類似于 select name,salary,count(1),max(salary) from user group by name,salary order by salary 等這樣的復雜性的統(tǒng)計需求,基本上不可能,或者說比較困難,所以我們在使用HBase的時候,一般都會借助二級索引的方案來進行實現(xiàn)。

HBase的一級索引就是rowkey,我們只能通過rowkey進行檢索。如果我們相對hbase里面列族的列列進行一些組合查詢,就需要采用HBase的二級索引方案來進行多條件的查詢。

1. MapReduce方案 2. ITHBASE(Indexed-Transanctional HBase)方案 3. IHBASE(Index HBase)方案 4. Hbase Coprocessor(協(xié)處理器)方案 5. Solr+hbase方案6. CCIndex(complementalclustering index)方案

常見的二級索引我們一般可以借助各種其他的方式來實現(xiàn),例如Phoenix或者solr或者ES等。

十二、HBase 調優(yōu)1. 通用優(yōu)化

NameNode的元數(shù)據(jù)備份使用SSD。

定時備份NameNode上的元數(shù)據(jù),每小時或者每天備份,如果數(shù)據(jù)極其重要,可以5~10分鐘備份一次。備份可以通過定時任務復制元數(shù)據(jù)目錄即可。

為NameNode指定多個元數(shù)據(jù)目錄,使用dfs.name.dir或者dfs.namenode.name.dir指定。一個指定本地磁盤,一個指定網絡磁盤。這樣可以提供元數(shù)據(jù)的冗余和健壯性,以免發(fā)生故障。

設置dfs.namenode.name.dir.restore為true,允許嘗試恢復之前失敗的dfs.namenode.name.dir目錄,在創(chuàng)建checkpoint時做此嘗試,如果設置了多個磁盤,建議允許。

NameNode節(jié)點必須配置為RAID1(鏡像盤)結構。

保持NameNode日志目錄有足夠的空間,這些日志有助于幫助你發(fā)現(xiàn)問題。

因為Hadoop是IO密集型框架,所以盡量提升存儲的速度和吞吐量(類似位寬)。

2. Linux優(yōu)化開啟文件系統(tǒng)的預讀緩存可以提高讀取速度

$ sudo blockdev --setra 32768 /dev/sda

提示:ra是readahead的縮寫

關閉進程睡眠池

$ sudo sysctl -w vm.swappiness=0

調整ulimit上限,默認值為比較小的數(shù)字

$ ulimit -n 查看允許最大進程數(shù)

$ ulimit -u 查看允許打開最大文件數(shù)

修改:

$ sudo vi /etc/security/limits.conf 修改打開文件數(shù)限制

末尾添加:

*                soft    nofile          1024000

*                hard    nofile          1024000

Hive             -       nofile          1024000
hive             -       nproc           1024000

$ sudo vi /etc/security/limits.d/20-nproc.conf 修改用戶打開進程數(shù)限制

修改為:

#*          soft    nproc     4096

#root       soft    nproc     unlimited

*          soft    nproc     40960

root       soft    nproc     unlimited

開啟集群的時間同步NTP。

更新系統(tǒng)補。ㄌ崾荆焊卵a丁前,請先測試新版本補丁對集群節(jié)點的兼容性)

3. HDFS優(yōu)化(hdfs-site.xml)保證RPC調用會有較多的線程數(shù)

屬性:dfs.namenode.handler.count

解釋:該屬性是NameNode服務默認線程數(shù),的默認值是10,根據(jù)機器的可用內存可以調整為50~100

屬性:dfs.datanode.handler.count

解釋:該屬性默認值為10,是DataNode的處理線程數(shù),如果HDFS客戶端程序讀寫請求比較多,可以調高到15~20,設置的值越大,內存消耗越多,不要調整的過高,一般業(yè)務中,5~10即可。

副本數(shù)的調整

屬性:dfs.replication

解釋:如果數(shù)據(jù)量巨大,且不是非常之重要,可以調整為2~3,如果數(shù)據(jù)非常之重要,可以調整為3~5。

文件塊大小的調整

屬性:dfs.blocksize

解釋:塊大小定義,該屬性應該根據(jù)存儲的大量的單個文件大小來設置,如果大量的單個文件都小于100M,建議設置成64M塊大小,對于大于100M或者達到GB的這種情況,建議設置成256M,一般設置范圍波動在64M~256M之間。

4. MapReduce優(yōu)化(mapred-site.xml)Job任務服務線程數(shù)調整

mapreduce.jobtracker.handler.count

該屬性是Job任務線程數(shù),默認值是10,根據(jù)機器的可用內存可以調整為50~100

Http服務器工作線程數(shù)

屬性:mapreduce.tasktracker.http.threads

解釋:定義HTTP服務器工作線程數(shù),默認值為40,對于大集群可以調整到80~100

文件排序合并優(yōu)化

屬性:mapreduce.task.io.sort.factor

解釋:文件排序時同時合并的數(shù)據(jù)流的數(shù)量,這也定義了同時打開文件的個數(shù),默認值為10,如果調高該參數(shù),可以明顯減少磁盤IO,即減少文件讀取的次數(shù)。

設置任務并發(fā)

屬性:mapreduce.map.speculative

解釋:該屬性可以設置任務是否可以并發(fā)執(zhí)行,如果任務多而小,該屬性設置為true可以明顯加快任務執(zhí)行效率,但是對于延遲非常高的任務,建議改為false,這就類似于迅雷下載。

MR輸出數(shù)據(jù)的壓縮

屬性:mapreduce.map.output.compress、mapreduce.output.fileoutputformat.compress

解釋:對于大集群而言,建議設置Map-Reduce的輸出為壓縮的數(shù)據(jù),而對于小集群,則不需要。

優(yōu)化Mapper和Reducer的個數(shù)

屬性:

mapreduce.tasktracker.map.tasks.maximum

mapreduce.tasktracker.reduce.tasks.maximum

解釋:以上兩個屬性分別為一個單獨的Job任務可以同時運行的Map和Reduce的數(shù)量。

設置上面兩個參數(shù)時,需要考慮CPU核數(shù)、磁盤和內存容量。假設一個8核的CPU,業(yè)務內容非常消耗CPU,那么可以設置map數(shù)量

4,如果該業(yè)務不是特別消耗CPU類型的,那么可以設置map數(shù)量為40,reduce數(shù)量為20。這些參數(shù)的值修改完成之后,一定要觀察是否有較長等待的任務,如果有的話,可以減少數(shù)量以加快任務執(zhí)行,如果設置一個很大的值,會引起大量的上下文切換,以及內存與磁盤之間的數(shù)據(jù)交換,這里沒有標準的配置數(shù)值,需要根據(jù)業(yè)務和硬件配置以及經驗來做出選擇。

在同一時刻,不要同時運行太多的MapReduce,這樣會消耗過多的內存,任務會執(zhí)行的非常緩慢,我們需要根據(jù)CPU核數(shù),內存容量設置一個MR任務并發(fā)的最大值,使固定數(shù)據(jù)量的任務完全加載到內存中,避免頻繁的內存和磁盤數(shù)據(jù)交換,從而降低磁盤IO,提高性能。

大概估算公式:map = 2 + 2/3cpu_core

reduce = 2 + 1/3cpu_core

5. HBase優(yōu)化在HDFS的文件中追加內容

HDFS 不是不允許追加內容么?沒錯,請看背景故事:

屬性:dfs.support.append

文件:hdfs-site.xml、hbase-site.xml

解釋:開啟HDFS追加同步,可以優(yōu)秀的配合HBase的數(shù)據(jù)同步和持久化。默認值為true。

優(yōu)化DataNode允許的最大文件打開數(shù)

屬性:dfs.datanode.max.transfer.threads

文件:hdfs-site.xml

解釋:HBase一般都會同一時間操作大量的文件,根據(jù)集群的數(shù)量和規(guī)模以及數(shù)據(jù)動作,設置為4096或者更高。默認值:4096

**優(yōu)化延遲高的數(shù)據(jù)操作的等待時間

屬性:dfs.image.transfer.timeout

文件:hdfs-site.xml

解釋:如果對于某一次數(shù)據(jù)操作來講,延遲非常高,socket需要等待更長的時間,建議把該值設置為更大的值(默認60000毫秒),以確保socket不會被timeout掉。

優(yōu)化數(shù)據(jù)的寫入效率

屬性:

mapreduce.map.output.compress

mapreduce.map.output.compress.codec

文件:mapred-site.xml

解釋:開啟這兩個數(shù)據(jù)可以大大提高文件的寫入效率,減少寫入時間。第一個屬性值修改為true,第二個屬性值修改為:org.apache.hadoop.io.compress.GzipCodec

優(yōu)化DataNode存儲

屬性:dfs.datanode.failed.volumes.tolerated

文件:hdfs-site.xml

解釋:默認為0,意思是當DataNode中有一個磁盤出現(xiàn)故障,則會認為該DataNode shutdown了。如果修改為1,則一個磁盤出現(xiàn)故障時,數(shù)據(jù)會被復制到其他正常的DataNode上,當前的DataNode繼續(xù)工作。

設置RPC監(jiān)聽數(shù)量

屬性:hbase.regionserver.handler.count

文件:hbase-site.xml

解釋:默認值為30,用于指定RPC監(jiān)聽的數(shù)量,可以根據(jù)客戶端的請求數(shù)進行調整,讀寫請求較多時,增加此值。

優(yōu)化HStore文件大小

屬性:hbase.hregion.max.filesize

文件:hbase-site.xml

解釋:默認值10737418240(10GB),如果需要運行HBase的MR任務,可以減小此值,因為一個region對應一個map任務,如果單個region過大,會導致map任務執(zhí)行時間過長。該值的意思就是,如果HFile的大小達到這個數(shù)值,則這個region會被切分為兩個Hfile。

優(yōu)化hbase客戶端緩存

屬性:hbase.client.write.buffer

文件:hbase-site.xml

解釋:用于指定HBase客戶端緩存,增大該值可以減少RPC調用次數(shù),但是會消耗更多內存,反之則反之。一般我們需要設定一定的緩存大小,以達到減少RPC次數(shù)的目的。

指定scan.next掃描HBase所獲取的行數(shù)

屬性:hbase.client.scanner.caching

文件:hbase-site.xml

解釋:用于指定scan.next方法獲取的默認行數(shù),值越大,消耗內存越大。

6. 內存優(yōu)化

HBase操作過程中需要大量的內存開銷,畢竟Table是可以緩存在內存中的,一般會分配整個可用內存的70%給HBase的Java堆。但是不建議分配非常大的堆內存,因為GC過程持續(xù)太久會導致RegionServer處于長期不可用狀態(tài),一般16~48G內存就可以了,如果因為框架占用內存過高導致系統(tǒng)內存不足,框架一樣會被系統(tǒng)服務拖死。

7. JVM優(yōu)化

涉及文件:hbase-env.sh

并行GC

參數(shù):·-XX:+UseParallelGC·

解釋:開啟并行GC

同時處理垃圾回收的線程數(shù)

參數(shù):-XX:ParallelGCThreads=cpu_core – 1

解釋:該屬性設置了同時處理垃圾回收的線程數(shù)。

禁用手動GC

參數(shù):-XX:DisableExplicitGC

解釋:防止開發(fā)人員手動調用GC

8. Zookeeper優(yōu)化優(yōu)化Zookeeper會話超時時間

參數(shù):zookeeper.session.timeout

文件:hbase-site.xml

解釋:In hbase-site.xml, set zookeeper.session.timeout to 30 seconds or less to bound failure detection (20-30 seconds is a good start)。

該值會直接關系到master發(fā)現(xiàn)服務器宕機的最大周期,默認值為30秒,如果該值過小,會在HBase在寫入大量數(shù)據(jù)發(fā)生而GC時,導致RegionServer短暫的不可用,從而沒有向ZK發(fā)送心跳包,最終導致認為從節(jié)點shutdown。一般20臺左右的集群需要配置5臺zookeeper。

十三、HBase 大廠面試題解析1. Hbase是怎么寫數(shù)據(jù)的?

Client寫入 -> 存入MemStore,一直到MemStore滿 -> Flush成一個StoreFile,直至增長到一定閾值 -> 觸發(fā)Compact合并操作 -> 多個StoreFile合并成一個StoreFile,同時進行版本合并和數(shù)據(jù)刪除 -> 當StoreFiles Compact后,逐步形成越來越大的StoreFile -> 單個StoreFile大小超過一定閾值后(默認10G),觸發(fā)Split操作,把當前Region Split成2個Region,Region會下線,新Split出的2個孩子Region會被HMaster分配到相應的HRegionServer 上,使得原先1個Region的壓力得以分流到2個Region上

由此過程可知,HBase只是增加數(shù)據(jù),沒有更新和刪除操作,用戶的更新和刪除都是邏輯層面的,在物理層面,更新只是追加操作,刪除只是標記操作。

用戶寫操作只需要進入到內存即可立即返回,從而保證I/O高性能。

2. HDFS和HBase各自使用場景

首先一點需要明白:Hbase是基于HDFS來存儲的。

HDFS:

一次性寫入,多次讀取。

保證數(shù)據(jù)的一致性。

主要是可以部署在許多廉價機器中,通過多副本提高可靠性,提供了容錯和恢復機制。

HBase:

瞬間寫入量很大,數(shù)據(jù)庫不好支撐或需要很高成本支撐的場景。

數(shù)據(jù)需要長久保存,且量會持久增長到比較大的場景。

HBase不適用與有 join,多級索引,表關系復雜的數(shù)據(jù)模型。

大數(shù)據(jù)量(100s TB級數(shù)據(jù))且有快速隨機訪問的需求。如:淘寶的交易歷史記錄。數(shù)據(jù)量巨大無容置疑,面向普通用戶的請求必然要即時響應。

業(yè)務場景簡單,不需要關系數(shù)據(jù)庫中很多特性(例如交叉列、交叉表,事務,連接等等)。

3. Hbase的存儲結構

Hbase 中的每張表都通過行鍵(rowkey)按照一定的范圍被分割成多個子表(HRegion),默認一個HRegion 超過256M 就要被分割成兩個,由HRegionServer管理,管理哪些 HRegion 由 Hmaster 分配。HRegion 存取一個子表時,會創(chuàng)建一個 HRegion 對象,然后對表的每個列族(Column Family)創(chuàng)建一個 store 實例, 每個 store 都會有 0 個或多個 StoreFile 與之對應,每個 StoreFile 都會對應一個HFile,HFile 就是實際的存儲文件,一個 HRegion 還擁有一個 MemStore實例。

4. 熱點現(xiàn)象(數(shù)據(jù)傾斜)怎么產生的,以及解決方法有哪些

熱點現(xiàn)象:

某個小的時段內,對HBase的讀寫請求集中到極少數(shù)的Region上,導致這些region所在的RegionServer處理請求量驟增,負載量明顯偏大,而其他的RgionServer明顯空閑。

熱點現(xiàn)象出現(xiàn)的原因:

HBase中的行是按照rowkey的字典順序排序的,這種設計優(yōu)化了scan操作,可以將相關的行以及會被一起讀取的行存取在臨近位置,便于scan。然而糟糕的rowkey設計是熱點的源頭。

熱點發(fā)生在大量的client直接訪問集群的一個或極少數(shù)個節(jié)點(訪問可能是讀,寫或者其他操作)。大量訪問會使熱點region所在的單個機器超出自身承受能力,引起性能下降甚至region不可用,這也會影響同一個RegionServer上的其他region,由于主機無法服務其他region的請求。

熱點現(xiàn)象解決辦法:

為了避免寫熱點,設計rowkey使得不同行在同一個region,但是在更多數(shù)據(jù)情況下,數(shù)據(jù)應該被寫入集群的多個region,而不是一個。常見的方法有以下這些:

加鹽:在rowkey的前面增加隨機數(shù),使得它和之前的rowkey的開頭不同。分配的前綴種類數(shù)量應該和你想使用數(shù)據(jù)分散到不同的region的數(shù)量一致。加鹽之后的rowkey就會根據(jù)隨機生成的前綴分散到各個region上,以避免熱點。

哈希:哈?梢允关撦d分散到整個集群,但是讀卻是可以預測的。使用確定的哈希可以讓客戶端重構完整的rowkey,可以使用get操作準確獲取某一個行數(shù)據(jù)

反轉:第三種防止熱點的方法時反轉固定長度或者數(shù)字格式的rowkey。這樣可以使得rowkey中經常改變的部分(最沒有意義的部分)放在前面。這樣可以有效的隨機rowkey,但是犧牲了rowkey的有序性。反轉rowkey的例子以手機號為rowkey,可以將手機號反轉后的字符串作為rowkey,這樣的就避免了以手機號那樣比較固定開頭導致熱點問題

時間戳反轉:一個常見的數(shù)據(jù)處理問題是快速獲取數(shù)據(jù)的最近版本,使用反轉的時間戳作為rowkey的一部分對這個問題十分有用,可以用 Long.Max_Value - timestamp 追加到key的末尾,例如[key][reverse_timestamp],[key]的最新值可以通過scan [key]獲得[key]的第一條記錄,因為HBase中rowkey是有序的,第一條記錄是最后錄入的數(shù)據(jù)。

比如需要保存一個用戶的操作記錄,按照操作時間倒序排序,在設計rowkey的時候,可以這樣設計[userId反轉][Long.Max_Value - timestamp],在查詢用戶的所有操作記錄數(shù)據(jù)的時候,直接指定反轉后的userId,startRow是[userId反轉][000000000000],stopRow是[userId反轉][Long.Max_Value - timestamp]

如果需要查詢某段時間的操作記錄,startRow是[user反轉][Long.Max_Value - 起始時間],stopRow是[userId反轉][Long.Max_Value - 結束時間]

HBase建表預分區(qū):創(chuàng)建HBase表時,就預先根據(jù)可能的RowKey劃分出多個region而不是默認的一個,從而可以將后續(xù)的讀寫操作負載均衡到不同的region上,避免熱點現(xiàn)象。

5. HBase的 rowkey 設計原則

長度原則:100字節(jié)以內,8的倍數(shù)最好,可能的情況下越短越好。因為HFile是按照 keyvalue 存儲的,過長的rowkey會影響存儲效率;其次,過長的rowkey在memstore中較大,影響緩沖效果,降低檢索效率。最后,操作系統(tǒng)大多為64位,8的倍數(shù),充分利用操作系統(tǒng)的最佳性能。

散列原則:高位散列,低位時間字段。避免熱點問題。

唯一原則:分利用這個排序的特點,將經常讀取的數(shù)據(jù)存儲到一塊,將最近可能會被訪問 的數(shù)據(jù)放到一塊。

6. HBase的列簇設計

原則:在合理范圍內能盡量少的減少列簇就盡量減少列簇,因為列簇是共享region的,每個列簇數(shù)據(jù)相差太大導致查詢效率低下。

最優(yōu):將所有相關性很強的 key-value 都放在同一個列簇下,這樣既能做到查詢效率最高,也能保持盡可能少的訪問不同的磁盤文件。以用戶信息為例,可以將必須的基本信息存放在一個列族,而一些附加的額外信息可以放在另一列族。

7. HBase 中 compact 用途是什么,什么時候觸發(fā),分為哪兩種,有什么區(qū)別

在 hbase 中每當有 memstore 數(shù)據(jù) flush 到磁盤之后,就形成一個 storefile,當 storeFile的數(shù)量達到一定程度后,就需要將 storefile 文件來進行 compaction 操作。

Compact 的作用:

合并文件

清除過期,多余版本的數(shù)據(jù)

提高讀寫數(shù)據(jù)的效率4HBase 中實現(xiàn)了兩種 compaction 的方式:minor and major. 這兩種 compaction 方式的區(qū)別是:

Minor 操作只用來做部分文件的合并操作以及包括 minVersion=0 并且設置 ttl 的過期版本清理,不做任何刪除數(shù)據(jù)、多版本數(shù)據(jù)的清理工作。

Major 操作是對 Region 下的 HStore 下的所有 StoreFile 執(zhí)行合并操作,最終的結果是整理合并出一個文件。

聲明: 本文由入駐維科號的作者撰寫,觀點僅代表作者本人,不代表OFweek立場。如有侵權或其他問題,請聯(lián)系舉報。

發(fā)表評論

0條評論,0人參與

請輸入評論內容...

請輸入評論/評論長度6~500個字

您提交的評論過于頻繁,請輸入驗證碼繼續(xù)

暫無評論

暫無評論

    掃碼關注公眾號
    OFweek人工智能網
    獲取更多精彩內容
    文章糾錯
    x
    *文字標題:
    *糾錯內容:
    聯(lián)系郵箱:
    *驗 證 碼:

    粵公網安備 44030502002758號