HBase 知識體系吐血總結
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í)行合并操作,最終的結果是整理合并出一個文件。

請輸入評論內容...
請輸入評論/評論長度6~500個字
最新活動更多
推薦專題
- 1 UALink規(guī)范發(fā)布:挑戰(zhàn)英偉達AI統(tǒng)治的開始
- 2 北電數(shù)智主辦酒仙橋論壇,探索AI產業(yè)發(fā)展新路徑
- 3 “AI寒武紀”爆發(fā)至今,五類新物種登上歷史舞臺
- 4 降薪、加班、裁員三重暴擊,“AI四小龍”已折戟兩家
- 5 國產智駕迎戰(zhàn)特斯拉FSD,AI含量差幾何?
- 6 光計算迎來商業(yè)化突破,但落地仍需時間
- 7 東陽光:2024年扭虧、一季度凈利大增,液冷疊加具身智能打開成長空間
- 8 地平線自動駕駛方案解讀
- 9 封殺AI“照騙”,“淘寶們”終于不忍了?
- 10 優(yōu)必選:營收大增主靠小件,虧損繼續(xù)又逢關稅,能否乘機器人東風翻身?