HBase 知識體系吐血總結(jié)
HBase 涉及的知識點(diǎn)如下圖所示,本文將逐一講解:
本文目錄如上圖
本文檔參考了關(guān)于 HBase 的官網(wǎng)及其他眾多資料整理而成,為了整潔的排版及舒適的閱讀,對于模糊不清晰的圖片及黑白圖片進(jìn)行重新繪制成了高清彩圖。
一、HBase 基礎(chǔ)1. HBase 基本介紹
簡介
HBase 是 BigTable 的開源 Java 版本。是建立在 HDFS 之上,提供高可靠性、高性能、列存儲、可伸縮、實(shí)時(shí)讀寫 NoSql 的數(shù)據(jù)庫系統(tǒng)。
它介于 NoSql 和 RDBMS 之間,僅能通過主鍵(row key)和主鍵的 range 來檢索數(shù)據(jù),僅支持單行事務(wù)(可通過 hive 支持來實(shí)現(xiàn)多表 join 等復(fù)雜操作)。
主要用來存儲結(jié)構(gòu)化和半結(jié)構(gòu)化的松散數(shù)據(jù)。
Hbase 查詢數(shù)據(jù)功能很簡單,不支持 join 等復(fù)雜操作,不支持復(fù)雜的事務(wù)(行級的事務(wù))Hbase 中支持的數(shù)據(jù)類型:byte[]與 hadoop 一樣,Hbase 目標(biāo)主要依靠橫向擴(kuò)展,通過不斷增加廉價(jià)的商用服務(wù)器,來增加計(jì)算和存儲能力。
HBase 中的表一般有這樣的特點(diǎn):
大:一個(gè)表可以有上十億行,上百萬列面向列:面向列(族)的存儲和權(quán)限控制,列(族)獨(dú)立檢索。稀疏:對于為空(null)的列,并不占用存儲空間,因此,表可以設(shè)計(jì)的非常稀疏。
HBase 的發(fā)展歷程
HBase 的原型是 Google 的 BigTable 論文,受到了該論文思想的啟發(fā),目前作為 Hadoop 的子項(xiàng)目來開發(fā)維護(hù),用于支持結(jié)構(gòu)化的數(shù)據(jù)存儲。
2006 年 Google 發(fā)表 BigTable 白皮書2006 年開始開發(fā) HBase2008 HBase 成為了 Hadoop 的子項(xiàng)目2010 年 HBase 成為 Apache 頂級項(xiàng)目2. HBase 與 Hadoop 的關(guān)系
HDFS
為分布式存儲提供文件系統(tǒng)針對存儲大尺寸的文件進(jìn)行優(yōu)化,不需要對 HDFS 上的文件進(jìn)行隨機(jī)讀寫直接使用文件數(shù)據(jù)模型不靈活使用文件系統(tǒng)和處理框架優(yōu)化一次寫入,多次讀取的方式
HBase
提供表狀的面向列的數(shù)據(jù)存儲針對表狀數(shù)據(jù)的隨機(jī)讀寫進(jìn)行優(yōu)化使用 key-value 操作數(shù)據(jù)提供靈活的數(shù)據(jù)模型使用表狀存儲,支持 MapReduce,依賴 HDFS優(yōu)化了多次讀,以及多次寫3. RDBMS 與 HBase 的對比
關(guān)系型數(shù)據(jù)庫
結(jié)構(gòu):
數(shù)據(jù)庫以表的形式存在支持 FAT、NTFS、EXT、文件系統(tǒng)使用 Commit log 存儲日志參考系統(tǒng)是坐標(biāo)系統(tǒng)使用主鍵(PK)支持分區(qū)使用行、列、單元格
功能:
支持向上擴(kuò)展使用 SQL 查詢面向行,即每一行都是一個(gè)連續(xù)單元數(shù)據(jù)總量依賴于服務(wù)器配置具有 ACID 支持適合結(jié)構(gòu)化數(shù)據(jù)傳統(tǒng)關(guān)系型數(shù)據(jù)庫一般都是中心化的支持事務(wù)支持 Join
HBase
結(jié)構(gòu):
數(shù)據(jù)庫以 region 的形式存在支持 HDFS 文件系統(tǒng)使用 WAL(Write-Ahead Logs)存儲日志參考系統(tǒng)是 Zookeeper使用行鍵(row key)支持分片使用行、列、列族和單元格
功能:
支持向外擴(kuò)展使用 API 和 MapReduce 來訪問 HBase 表數(shù)據(jù)面向列,即每一列都是一個(gè)連續(xù)的單元數(shù)據(jù)總量不依賴具體某臺機(jī)器,而取決于機(jī)器數(shù)量HBase 不支持 ACID(Atomicity、Consistency、Isolation、Durability)適合結(jié)構(gòu)化數(shù)據(jù)和非結(jié)構(gòu)化數(shù)據(jù)一般都是分布式的HBase 不支持事務(wù)不支持 Join4. HBase 特征簡要海量存儲
Hbase 適合存儲 PB 級別的海量數(shù)據(jù),在 PB 級別的數(shù)據(jù)以及采用廉價(jià) PC 存儲的情況下,能在幾十到百毫秒內(nèi)返回?cái)?shù)據(jù)。這與 Hbase 的極易擴(kuò)展性息息相關(guān)。正式因?yàn)?Hbase 良好的擴(kuò)展性,才為海量數(shù)據(jù)的存儲提供了便利。
列式存儲
這里的列式存儲其實(shí)說的是列族存儲,Hbase 是根據(jù)列族來存儲數(shù)據(jù)的。列族下面可以有非常多的列,列族在創(chuàng)建表的時(shí)候就必須指定。
極易擴(kuò)展
Hbase 的擴(kuò)展性主要體現(xiàn)在兩個(gè)方面,一個(gè)是基于上層處理能力(RegionServer)的擴(kuò)展,一個(gè)是基于存儲的擴(kuò)展(HDFS)。通過橫向添加 RegionSever 的機(jī)器,進(jìn)行水平擴(kuò)展,提升 Hbase 上層的處理能力,提升 Hbsae 服務(wù)更多 Region 的能力。備注:RegionServer 的作用是管理 region、承接業(yè)務(wù)的訪問,這個(gè)后面會詳細(xì)的介紹通過橫向添加 Datanode 的機(jī)器,進(jìn)行存儲層擴(kuò)容,提升 Hbase 的數(shù)據(jù)存儲能力和提升后端存儲的讀寫能力。
高并發(fā)
由于目前大部分使用 Hbase 的架構(gòu),都是采用的廉價(jià) PC,因此單個(gè) IO 的延遲其實(shí)并不小,一般在幾十到上百 ms 之間。這里說的高并發(fā),主要是在并發(fā)的情況下,Hbase 的單個(gè) IO 延遲下降并不多。能獲得高并發(fā)、低延遲的服務(wù)。
稀疏
稀疏主要是針對 Hbase 列的靈活性,在列族中,你可以指定任意多的列,在列數(shù)據(jù)為空的情況下,是不會占用存儲空間的。
二、HBase 基礎(chǔ)架構(gòu)
HMaster
功能:
監(jiān)控 RegionServer處理 RegionServer 故障轉(zhuǎn)移處理元數(shù)據(jù)的變更處理 region 的分配或移除在空閑時(shí)間進(jìn)行數(shù)據(jù)的負(fù)載均衡通過 Zookeeper 發(fā)布自己的位置給客戶端RegionServer
功能:
負(fù)責(zé)存儲 HBase 的實(shí)際數(shù)據(jù)處理分配給它的 Region刷新緩存到 HDFS維護(hù) HLog執(zhí)行壓縮負(fù)責(zé)處理 Region 分片
組件:
Write-Ahead logs
HBase 的修改記錄,當(dāng)對 HBase 讀寫數(shù)據(jù)的時(shí)候,數(shù)據(jù)不是直接寫進(jìn)磁盤,它會在內(nèi)存中保留一段時(shí)間(時(shí)間以及數(shù)據(jù)量閾值可以設(shè)定)。但把數(shù)據(jù)保存在內(nèi)存中可能有更高的概率引起數(shù)據(jù)丟失,為了解決這個(gè)問題,數(shù)據(jù)會先寫在一個(gè)叫做 Write-Ahead logfile 的文件中,然后再寫入內(nèi)存中。所以在系統(tǒng)出現(xiàn)故障的時(shí)候,數(shù)據(jù)可以通過這個(gè)日志文件重建。
HFile
這是在磁盤上保存原始數(shù)據(jù)的實(shí)際的物理文件,是實(shí)際的存儲文件。
Store
HFile 存儲在 Store 中,一個(gè) Store 對應(yīng) HBase 表中的一個(gè)列族。
MemStore
顧名思義,就是內(nèi)存存儲,位于內(nèi)存中,用來保存當(dāng)前的數(shù)據(jù)操作,所以當(dāng)數(shù)據(jù)保存在 WAL 中之后,RegsionServer 會在內(nèi)存中存儲鍵值對。
Region
Hbase 表的分片,HBase 表會根據(jù) RowKey 值被切分成不同的 region 存儲在 RegionServer 中,在一個(gè) RegionServer 中可以有多個(gè)不同的 region。
三、HBase 常用 shell 操作1) 添加操作進(jìn)入 HBase 客戶端命令操作界面$ bin/hbase shell
查看幫助命令hbase(main):001:0> help
查看當(dāng)前數(shù)據(jù)庫中有哪些表hbase(main):002:0> list
創(chuàng)建一張表
創(chuàng)建 user 表,包含 info、data 兩個(gè)列族
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 列標(biāo)示符,值為 zhangsan
hbase(main):011:0> put 'user', 'rk0001', 'info:name', 'zhangsan'
向 user 表中插入信息,row key 為 rk0001,列族 info 中添加 gender 列標(biāo)示符,值為 female
hbase(main):012:0> put 'user', 'rk0001', 'info:gender', 'female'
向 user 表中插入信息,row key 為 rk0001,列族 info 中添加 age 列標(biāo)示符,值為 20
hbase(main):013:0> put 'user', 'rk0001', 'info:age', 20
向 user 表中插入信息,row key 為 rk0001,列族 data 中添加 pic 列標(biāo)示符,值為 picture
hbase(main):014:0> put 'user', 'rk0001', 'data:pic', 'picture'
2) 查詢操作通過 rowkey 進(jìn)行查詢
獲取 user 表中 row key 為 rk0001 的所有信息
hbase(main):015:0> get 'user', 'rk0001'
查看 rowkey 下面的某個(gè)列族的信息
獲取 user 表中 row key 為 rk0001,info 列族的所有信息
hbase(main):016:0> get 'user', 'rk0001', 'info'
查看 rowkey 指定列族指定字段的值
獲取 user 表中 row key 為 rk0001,info 列族的 name、age 列標(biāo)示符的信息
hbase(main):017:0> get 'user', 'rk0001', 'info:name', 'info:age'
查看 rowkey 指定多個(gè)列族的信息
獲取 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,列標(biāo)示符中含有 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']}
指定列族與某個(gè)列名查詢
查詢 user 表中列族為 info、列標(biāo)示符為 name 的信息
scan 'user', {COLUMNS => 'info:name'}
指定列族與列名以及限定版本查詢
查詢 user 表中列族為 info、列標(biāo)示符為 name 的信息,并且版本最新的 5 個(gè)
scan 'user', {COLUMNS => 'info:name', VERSIONS => 5}
指定多個(gè)列族與按照數(shù)據(jù)值模糊查詢
查詢 user 表中列族為 info 和 data 且列標(biāo)示符中含有 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)計(jì)一張表有多少行數(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 以及列名進(jìn)行刪除
刪除 user 表 row key 為 rk0001,列標(biāo)示符為 info:name 的數(shù)據(jù)
hbase(main):045:0> delete 'user', 'rk0001', 'info:name'
指定 rowkey,列名以及字段值進(jìn)行刪除
刪除 user 表 row key 為 rk0001,列標(biāo)示符為 info:name,timestamp 為 1392383705316 的數(shù)據(jù)
delete 'user', 'rk0001', 'info:name', 1392383705316
刪除一個(gè)列族
刪除一個(gè)列族
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 這個(gè)表,使用命令:
hbase(main):050:0> drop 'user'
注意:如果直接 drop 表,會報(bào)錯(cuò):Drop the named table. Table must first be disabled
四、HBase 的高級 shell 管理命令status
例如:顯示服務(wù)器狀態(tài)
hbase(main):058:0> status 'node01'
whoami
顯示 HBase 當(dāng)前用戶,例如:
hbase> whoami
list
顯示當(dāng)前所有的表
hbase> list
count
統(tǒng)計(jì)指定表的記錄數(shù),例如:
hbase> count 'user'
describe
展示表結(jié)構(gòu)信息
hbase> describe 'user'
exists
檢查表是否存在,適用于表量特別多的情況
hbase> exists 'user'
is_enabled、is_disabled
檢查表是否啟用或禁用
hbase> is_enabled 'user'
alter
該命令可以改變表和列族的模式,例如:
為當(dāng)前表增加列族:
hbase> alter 'user', NAME => 'CF2', VERSIONS => 2
為當(dāng)前表刪除列族:
hbase(main):002:0> alter 'user', 'delete' => 'CF2'
disable/enable
禁用一張表/啟用一張表
drop
刪除一張表,記得在刪除表之前必須先禁用
truncate
清空表
2. 過濾器查詢
過濾器的類型很多,但是可以分為兩大類——比較過濾器,專用過濾器。
過濾器的作用是在服務(wù)端判斷數(shù)據(jù)是否滿足條件,然后只將滿足條件的數(shù)據(jù)返回給客戶端;
hbase 過濾器的比較運(yùn)算符:
LESS <
LESS_OR_EQUAL <=
EQUAL =
NOT_EQUAL <>
GREATER_OR_EQUAL >=
GREATER >
NO_OP 排除所有
Hbase 過濾器的比較器(指定比較機(jī)制):
BinaryComparator 按字節(jié)索引順序比較指定字節(jié)數(shù)組,采用Bytes.compareTo(byte[])
BinaryPrefixComparator 跟前面相同,只是比較左端的數(shù)據(jù)是否相同
NullComparator 判斷給定的是否為空
BitComparator 按位比較
RegexStringComparator 提供一個(gè)正則的比較器,僅支持 EQUAL 和非EQUAL
SubstringComparator 判斷提供的子串是否出現(xiàn)在value中。
1) 比較過濾器rowKey 過濾器 RowFilter
通過 RowFilter 過濾比 rowKey 0003 小的所有值出來
@Test
public void rowKeyFilter() throws IOException {
列族過濾器 FamilyFilter
查詢比 f2 列族小的所有的列族內(nèi)的數(shù)據(jù)
@Test
public void familyFilter() throws IOException {
列過濾器 QualifierFilter
只查詢 name 列的值
@Test
public void qualifierFilter() throws IOException {
列值過濾器 ValueFilter
查詢所有列當(dāng)中包含 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ù),并且同時(shí)滿足 rowkey 的前綴以 00 開頭的數(shù)據(jù)(PrefixFilter)
六、HBase 底層原理1. 系統(tǒng)架構(gòu)
HBase系統(tǒng)架構(gòu)
根據(jù)這幅圖,解釋下HBase中各個(gè)組件
1) Client包含訪問hbase的接口,Client維護(hù)著一些cache來加快對hbase的訪問,比如regione的位置信息.2) Zookeeper
HBase可以使用內(nèi)置的Zookeeper,也可以使用外置的,在實(shí)際生產(chǎn)環(huán)境,為了保持統(tǒng)一性,一般使用外置Zookeeper。
Zookeeper在HBase中的作用:
保證任何時(shí)候,集群中只有一個(gè)master存貯所有Region的尋址入口實(shí)時(shí)監(jiān)控Region Server的狀態(tài),將Region server的上線和下線信息實(shí)時(shí)通知給Master3) HMaster為Region server分配region負(fù)責(zé)region server的負(fù)載均衡發(fā)現(xiàn)失效的region server并重新分配其上的regionHDFS上的垃圾文件回收處理schema更新請求4) HRegion ServerHRegion server維護(hù)HMaster分配給它的region,處理對這些region的IO請求HRegion server負(fù)責(zé)切分在運(yùn)行過程中變得過大的region從圖中可以看到,Client訪問HBase上數(shù)據(jù)的過程并不需要HMaster參與(尋址訪問Zookeeper和HRegion server,數(shù)據(jù)讀寫訪問HRegione server)
HMaster僅僅維護(hù)者table和HRegion的元數(shù)據(jù)信息,負(fù)載很低。
2. HBase的表數(shù)據(jù)模型
HBase的表結(jié)構(gòu)1) 行鍵 Row Key
與nosql數(shù)據(jù)庫一樣,row key是用來檢索記錄的主鍵。訪問hbase table中的行,只有三種方式:
通過單個(gè)row key訪問通過row key的range全表掃描
Row Key 行鍵可以是任意字符串(最大長度是 64KB,實(shí)際應(yīng)用中長度一般為 10-100bytes),在hbase內(nèi)部,row key保存為字節(jié)數(shù)組。
Hbase會對表中的數(shù)據(jù)按照rowkey排序(字典順序)
存儲時(shí),數(shù)據(jù)按照Row key的字典序(byte order)排序存儲。設(shè)計(jì)key時(shí),要充分排序存儲這個(gè)特性,將經(jīng)常一起讀取的行存儲放到一起。(位置相關(guān)性)。
注意:字典序?qū)nt排序的結(jié)果是1,10,100,11,12,13,14,15,16,17,18,19,2,20,21 ... 。要保持整形的自然序,行鍵必須用0作左填充。
行的一次讀寫是原子操作 (不論一次讀寫多少列)。這個(gè)設(shè)計(jì)決策能夠使用戶很容易的理解程序在對同一個(gè)行進(jìn)行并發(fā)更新操作時(shí)的行為。
2) 列族 Column Family
HBase表中的每個(gè)列,都?xì)w屬于某個(gè)列族。列族是表的schema的一部分(而列不是),必須在使用表之前定義。
列名都以列族作為前綴。例如 courses:history , courses:math 都屬于 courses 這個(gè)列族。
訪問控制、磁盤和內(nèi)存的使用統(tǒng)計(jì)都是在列族層面進(jìn)行的。列族越多,在取一行數(shù)據(jù)時(shí)所要參與IO、搜尋的文件就越多,所以,如果沒有必要,不要設(shè)置太多的列族。
3) 列 Column
列族下面的具體列,屬于某一個(gè)ColumnFamily,類似于在mysql當(dāng)中創(chuàng)建的具體的列。
4) 時(shí)間戳 Timestamp
HBase中通過row和columns確定的為一個(gè)存貯單元稱為cell。每個(gè) cell都保存著同一份數(shù)據(jù)的多個(gè)版本。版本通過時(shí)間戳來索引。時(shí)間戳的類型是 64位整型。時(shí)間戳可以由hbase(在數(shù)據(jù)寫入時(shí)自動(dòng) )賦值,此時(shí)時(shí)間戳是精確到毫秒的當(dāng)前系統(tǒng)時(shí)間。時(shí)間戳也可以由客戶顯式賦值。如果應(yīng)用程序要避免數(shù)據(jù)版本沖突,就必須自己生成具有唯一性的時(shí)間戳。每個(gè) cell中,不同版本的數(shù)據(jù)按照時(shí)間倒序排序,即最新的數(shù)據(jù)排在最前面。
為了避免數(shù)據(jù)存在過多版本造成的的管理 (包括存貯和索引)負(fù)擔(dān),hbase提供了兩種數(shù)據(jù)版本回收方式:
保存數(shù)據(jù)的最后n個(gè)版本保存最近一段時(shí)間內(nèi)的版本(設(shè)置數(shù)據(jù)的生命周期TTL)。
用戶可以針對每個(gè)列族進(jìn)行設(shè)置。
5) 單元 Cell
由{row key, column( =<family> + <label>), version} 唯一確定的單元。cell中的數(shù)據(jù)是沒有類型的,全部是字節(jié)碼形式存貯。
6) 版本號 VersionNum
數(shù)據(jù)的版本號,每條數(shù)據(jù)可以有多個(gè)版本號,默認(rèn)值為系統(tǒng)時(shí)間戳,類型為Long。
3. 物理存儲1) 整體結(jié)構(gòu)
HBase 整體結(jié)構(gòu)
Table 中的所有行都按照 Row Key 的字典序排列。
Table 在行的方向上分割為多個(gè) HRegion。
HRegion按大小分割的(默認(rèn)10G),每個(gè)表一開始只有一 個(gè)HRegion,隨著數(shù)據(jù)不斷插入表,HRegion不斷增大,當(dāng)增大到一個(gè)閥值的時(shí)候,HRegion就會等分會兩個(gè)新的HRegion。當(dāng)Table 中的行不斷增多,就會有越來越多的 HRegion。
HRegion 是 HBase 中分布式存儲和負(fù)載均衡的最小單元。最小單元就表示不同的 HRegion 可以分布在不同的 HRegion Server 上。但一個(gè) HRegion 是不會拆分到多個(gè) Server 上的。
HRegion 雖然是負(fù)載均衡的最小單元,但并不是物理存儲的最小單元。事實(shí)上,HRegion 由一個(gè)或者多個(gè) Store 組成,每個(gè) Store 保存一個(gè) Column Family。每個(gè) Strore 又由一個(gè) MemStore 和0至多個(gè) StoreFile 組成。如上圖。
2) StoreFile 和 HFile 結(jié)構(gòu)
StoreFile以HFile格式保存在HDFS上。
HFile的格式為:
HFile 格式
首先HFile文件是不定長的,長度固定的只有其中的兩塊:Trailer和FileInfo。正如圖中所示的,Trailer中有指針指向其他數(shù) 據(jù)塊的起始點(diǎn)。
File Info中記錄了文件的一些Meta信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等。
Data Index和Meta Index塊記錄了每個(gè)Data塊和Meta塊的起始點(diǎn)。
Data Block是HBase I/O的基本單元,為了提高效率,HRegionServer中有基于LRU的Block Cache機(jī)制。每個(gè)Data塊的大小可以在創(chuàng)建一個(gè)Table的時(shí)候通過參數(shù)指定,大號的Block有利于順序Scan,小號Block利于隨機(jī)查詢。每個(gè)Data塊除了開頭的Magic以外就是一個(gè)個(gè)KeyValue對拼接而成, Magic內(nèi)容就是一些隨機(jī)數(shù)字,目的是防止數(shù)據(jù)損壞。
HFile里面的每個(gè)KeyValue對就是一個(gè)簡單的byte數(shù)組。但是這個(gè)byte數(shù)組里面包含了很多項(xiàng),并且有固定的結(jié)構(gòu)。我們來看看里面的具體結(jié)構(gòu):
HFile 具體結(jié)構(gòu)
開始是兩個(gè)固定長度的數(shù)值,分別表示Key的長度和Value的長度。緊接著是Key,開始是固定長度的數(shù)值,表示RowKey的長度,緊接著是 RowKey,然后是固定長度的數(shù)值,表示Family的長度,然后是Family,接著是Qualifier,然后是兩個(gè)固定長度的數(shù)值,表示Time Stamp和Key Type(Put/Delete)。Value部分沒有這么復(fù)雜的結(jié)構(gòu),就是純粹的二進(jìn)制數(shù)據(jù)了。
HFile分為六個(gè)部分:
Data Block 段–保存表中的數(shù)據(jù),這部分可以被壓縮.
Meta Block 段 (可選的)–保存用戶自定義的kv對,可以被壓縮。
File Info 段–Hfile的元信息,不被壓縮,用戶也可以在這一部分添加自己的元信息。
Data Block Index 段–Data Block的索引。每條索引的key是被索引的block的第一條記錄的key。
Meta Block Index段 (可選的)–Meta Block的索引。
Trailer–這一段是定長的。保存了每一段的偏移量,讀取一個(gè)HFile時(shí),會首先讀取Trailer,Trailer保存了每個(gè)段的起始位置(段的Magic Number用來做安全check),然后,DataBlock Index會被讀取到內(nèi)存中,這樣,當(dāng)檢索某個(gè)key時(shí),不需要掃描整個(gè)HFile,而只需從內(nèi)存中找到key所在的block,通過一次磁盤io將整個(gè) block讀取到內(nèi)存中,再找到需要的key。DataBlock Index采用LRU機(jī)制淘汰。
HFile的Data Block,Meta Block通常采用壓縮方式存儲,壓縮之后可以大大減少網(wǎng)絡(luò)IO和磁盤IO,隨之而來的開銷當(dāng)然是需要花費(fèi)cpu進(jìn)行壓縮和解壓縮。目前HFile的壓縮支持兩種方式:Gzip,Lzo。
3) Memstore與StoreFile
一個(gè) HRegion 由多個(gè) Store 組成,每個(gè) Store 包含一個(gè)列族的所有數(shù)據(jù)Store 包括位于內(nèi)存的 Memstore 和位于硬盤的 StoreFile。
寫操作先寫入 Memstore,當(dāng) Memstore 中的數(shù)據(jù)量達(dá)到某個(gè)閾值,HRegionServer 啟動(dòng) FlashCache 進(jìn)程寫入 StoreFile,每次寫入形成單獨(dú)一個(gè) StoreFile
當(dāng) StoreFile 大小超過一定閾值后,會把當(dāng)前的 HRegion 分割成兩個(gè),并由 HMaster 分配給相應(yīng)的 HRegion 服務(wù)器,實(shí)現(xiàn)負(fù)載均衡
客戶端檢索數(shù)據(jù)時(shí),先在memstore找,找不到再找storefile。
4) HLog(WAL log)
WAL 意為Write ahead log,類似 mysql 中的 binlog,用來 做災(zāi)難恢復(fù)時(shí)用,Hlog記錄數(shù)據(jù)的所有變更,一旦數(shù)據(jù)修改,就可以從log中進(jìn)行恢復(fù)。
每個(gè)Region Server維護(hù)一個(gè)Hlog,而不是每個(gè)Region一個(gè)。這樣不同region(來自不同table)的日志會混在一起,這樣做的目的是不斷追加單個(gè)文件相對于同時(shí)寫多個(gè)文件而言,可以減少磁盤尋址次數(shù),因此可以提高對table的寫性能。帶來的麻煩是,如果一臺region server下線,為了恢復(fù)其上的region,需要將region server上的log進(jìn)行拆分,然后分發(fā)到其它region server上進(jìn)行恢復(fù)。
HLog文件就是一個(gè)普通的Hadoop Sequence File:
HLog Sequence File 的Key是HLogKey對象,HLogKey中記錄了寫入數(shù)據(jù)的歸屬信息,除了table和region名字外,同時(shí)還包括 sequence number和timestamp,timestamp是”寫入時(shí)間”,sequence number的起始值為0,或者是最近一次存入文件系統(tǒng)中sequence number。HLog Sequece File的Value是HBase的KeyValue對象,即對應(yīng)HFile中的KeyValue,可參見上文描述。4. 讀寫過程
1) 讀請求過程:
HRegionServer保存著meta表以及表數(shù)據(jù),要訪問表數(shù)據(jù),首先Client先去訪問zookeeper,從zookeeper里面獲取meta表所在的位置信息,即找到這個(gè)meta表在哪個(gè)HRegionServer上保存著。
接著Client通過剛才獲取到的HRegionServer的IP來訪問Meta表所在的HRegionServer,從而讀取到Meta,進(jìn)而獲取到Meta表中存放的元數(shù)據(jù)。
Client通過元數(shù)據(jù)中存儲的信息,訪問對應(yīng)的HRegionServer,然后掃描所在HRegionServer的Memstore和Storefile來查詢數(shù)據(jù)。
最后HRegionServer把查詢到的數(shù)據(jù)響應(yīng)給Client。
查看meta表信息
hbase(main):011:0> scan 'hbase:meta'
2) 寫請求過程:
Client也是先訪問zookeeper,找到Meta表,并獲取Meta表元數(shù)據(jù)。
確定當(dāng)前將要寫入的數(shù)據(jù)所對應(yīng)的HRegion和HRegionServer服務(wù)器。
Client向該HRegionServer服務(wù)器發(fā)起寫入數(shù)據(jù)請求,然后HRegionServer收到請求并響應(yīng)。
Client先把數(shù)據(jù)寫入到HLog,以防止數(shù)據(jù)丟失。
然后將數(shù)據(jù)寫入到Memstore。
如果HLog和Memstore均寫入成功,則這條數(shù)據(jù)寫入成功
如果Memstore達(dá)到閾值,會把Memstore中的數(shù)據(jù)flush到Storefile中。
當(dāng)Storefile越來越多,會觸發(fā)Compact合并操作,把過多的Storefile合并成一個(gè)大的Storefile。
當(dāng)Storefile越來越大,Region也會越來越大,達(dá)到閾值后,會觸發(fā)Split操作,將Region一分為二。
細(xì)節(jié)描述:
HBase使用MemStore和StoreFile存儲對表的更新。數(shù)據(jù)在更新時(shí)首先寫入Log(WAL log)和內(nèi)存(MemStore)中,MemStore中的數(shù)據(jù)是排序的,當(dāng)MemStore累計(jì)到一定閾值時(shí),就會創(chuàng)建一個(gè)新的MemStore,并且將老的MemStore添加到flush隊(duì)列,由單獨(dú)的線程flush到磁盤上,成為一個(gè)StoreFile。于此同時(shí),系統(tǒng)會在zookeeper中記錄一個(gè)redo point,表示這個(gè)時(shí)刻之前的變更已經(jīng)持久化了。當(dāng)系統(tǒng)出現(xiàn)意外時(shí),可能導(dǎo)致內(nèi)存(MemStore)中的數(shù)據(jù)丟失,此時(shí)使用Log(WAL log)來恢復(fù)checkpoint之后的數(shù)據(jù)。
StoreFile是只讀的,一旦創(chuàng)建后就不可以再修改。因此HBase的更新其實(shí)是不斷追加的操作。當(dāng)一個(gè)Store中的StoreFile達(dá)到一定的閾值后,就會進(jìn)行一次合并(minor_compact, major_compact),將對同一個(gè)key的修改合并到一起,形成一個(gè)大的StoreFile,當(dāng)StoreFile的大小達(dá)到一定閾值后,又會對 StoreFile進(jìn)行split,等分為兩個(gè)StoreFile。
由于對表的更新是不斷追加的,compact時(shí),需要訪問Store中全部的 StoreFile和MemStore,將他們按row key進(jìn)行合并,由于StoreFile和MemStore都是經(jīng)過排序的,并且StoreFile帶有內(nèi)存中索引,合并的過程還是比較快。
5. HRegion管理1) HRegion分配
任何時(shí)刻,一個(gè)HRegion只能分配給一個(gè)HRegion Server。HMaster記錄了當(dāng)前有哪些可用的HRegion Server。以及當(dāng)前哪些HRegion分配給了哪些HRegion Server,哪些HRegion還沒有分配。當(dāng)需要分配的新的HRegion,并且有一個(gè)HRegion Server上有可用空間時(shí),HMaster就給這個(gè)HRegion Server發(fā)送一個(gè)裝載請求,把HRegion分配給這個(gè)HRegion Server。HRegion Server得到請求后,就開始對此HRegion提供服務(wù)。
2) HRegion Server上線
HMaster使用zookeeper來跟蹤HRegion Server狀態(tài)。當(dāng)某個(gè)HRegion Server啟動(dòng)時(shí),會首先在zookeeper上的server目錄下建立代表自己的znode。由于HMaster訂閱了server目錄上的變更消息,當(dāng)server目錄下的文件出現(xiàn)新增或刪除操作時(shí),HMaster可以得到來自zookeeper的實(shí)時(shí)通知。因此一旦HRegion Server上線,HMaster能馬上得到消息。
3) HRegion Server下線
當(dāng)HRegion Server下線時(shí),它和zookeeper的會話斷開,zookeeper而自動(dòng)釋放代表這臺server的文件上的獨(dú)占鎖。HMaster就可以確定:
HRegion Server和zookeeper之間的網(wǎng)絡(luò)斷開了。HRegion Server掛了。
無論哪種情況,HRegion Server都無法繼續(xù)為它的HRegion提供服務(wù)了,此時(shí)HMaster會刪除server目錄下代表這臺HRegion Server的znode數(shù)據(jù),并將這臺HRegion Server的HRegion分配給其它還活著的節(jié)點(diǎn)。
6. HMaster工作機(jī)制1) master上線
master啟動(dòng)進(jìn)行以下步驟:
從zookeeper上獲取唯一一個(gè)代表active master的鎖,用來阻止其它HMaster成為master。掃描zookeeper上的server父節(jié)點(diǎn),獲得當(dāng)前可用的HRegion Server列表。和每個(gè)HRegion Server通信,獲得當(dāng)前已分配的HRegion和HRegion Server的對應(yīng)關(guān)系。掃描.META.region的集合,計(jì)算得到當(dāng)前還未分配的HRegion,將他們放入待分配HRegion列表。2) master下線
由于HMaster只維護(hù)表和region的元數(shù)據(jù),而不參與表數(shù)據(jù)IO的過程,HMaster下線僅導(dǎo)致所有元數(shù)據(jù)的修改被凍結(jié)(無法創(chuàng)建刪除表,無法修改表的schema,無法進(jìn)行HRegion的負(fù)載均衡,無法處理HRegion 上下線,無法進(jìn)行HRegion的合并,唯一例外的是HRegion的split可以正常進(jìn)行,因?yàn)橹挥蠬Region Server參與),表的數(shù)據(jù)讀寫還可以正常進(jìn)行。因此HMaster下線短時(shí)間內(nèi)對整個(gè)HBase集群沒有影響。
從上線過程可以看到,HMaster保存的信息全是可以冗余信息(都可以從系統(tǒng)其它地方收集到或者計(jì)算出來)
因此,一般HBase集群中總是有一個(gè)HMaster在提供服務(wù),還有一個(gè)以上的‘HMaster’在等待時(shí)機(jī)搶占它的位置。
7. HBase三個(gè)重要機(jī)制1) flush機(jī)制
1.(hbase.regionserver.global.memstore.size)默認(rèn);堆大小的40%regionServer的全局memstore的大小,超過該大小會觸發(fā)flush到磁盤的操作,默認(rèn)是堆大小的40%,而且regionserver級別的flush會阻塞客戶端讀寫
2.(hbase.hregion.memstore.flush.size)默認(rèn):128M單個(gè)region里memstore的緩存大小,超過那么整個(gè)HRegion就會flush,
3.(hbase.regionserver.optionalcacheflushinterval)默認(rèn):1h內(nèi)存中的文件在自動(dòng)刷新之前能夠存活的最長時(shí)間
4.(hbase.regionserver.global.memstore.size.lower.limit)默認(rèn):堆大小 * 0.4 * 0.95有時(shí)候集群的“寫負(fù)載”非常高,寫入量一直超過flush的量,這時(shí),我們就希望memstore不要超過一定的安全設(shè)置。在這種情況下,寫操作就要被阻塞一直到memstore恢復(fù)到一個(gè)“可管理”的大小, 這個(gè)大小就是默認(rèn)值是堆大小 * 0.4 * 0.95,也就是當(dāng)regionserver級別的flush操作發(fā)送后,會阻塞客戶端寫,一直阻塞到整個(gè)regionserver級別的memstore的大小為 堆大小 * 0.4 *0.95為止
5.(hbase.hregion.preclose.flush.size)默認(rèn)為:5M當(dāng)一個(gè) region 中的 memstore 的大小大于這個(gè)值的時(shí)候,我們又觸發(fā)了region的 close時(shí),會先運(yùn)行“pre-flush”操作,清理這個(gè)需要關(guān)閉的memstore,然后 將這個(gè) region 下線。當(dāng)一個(gè) region 下線了,我們無法再進(jìn)行任何寫操作。如果一個(gè) memstore 很大的時(shí)候,flush 操作會消耗很多時(shí)間。"pre-flush" 操作意味著在 region 下線之前,會先把 memstore 清空。這樣在最終執(zhí)行 close 操作的時(shí)候,flush 操作會很快。
6.(hbase.hstore.compactionThreshold)默認(rèn):超過3個(gè)一個(gè)store里面允許存的hfile的個(gè)數(shù),超過這個(gè)個(gè)數(shù)會被寫到新的一個(gè)hfile里面 也即是每個(gè)region的每個(gè)列族對應(yīng)的memstore在flush為hfile的時(shí)候,默認(rèn)情況下當(dāng)超過3個(gè)hfile的時(shí)候就會對這些文件進(jìn)行合并重寫為一個(gè)新文件,設(shè)置個(gè)數(shù)越大可以減少觸發(fā)合并的時(shí)間,但是每次合并的時(shí)間就會越長
2) compact機(jī)制
把小的storeFile文件合并成大的HFile文件。清理過期的數(shù)據(jù),包括刪除的數(shù)據(jù)將數(shù)據(jù)的版本號保存為1個(gè)。
3) split機(jī)制
當(dāng)HRegion達(dá)到閾值,會把過大的HRegion一分為二。默認(rèn)一個(gè)HFile達(dá)到10Gb的時(shí)候就會進(jìn)行切分。
七、HBase 與 MapReduce 的集成
HBase 當(dāng)中的數(shù)據(jù)最終都是存儲在 HDFS 上面的,HBase 天生的支持 MR 的操作,我們可以通過 MR 直接處理 HBase 當(dāng)中的數(shù)據(jù),并且 MR 可以將處理后的結(jié)果直接存儲到 HBase 當(dāng)中去。
需求:讀取 HBase 當(dāng)中一張表的數(shù)據(jù),然后將數(shù)據(jù)寫入到 HBase 當(dāng)中的另外一張表當(dāng)中去。
注意:我們可以使用 TableMapper 與 TableReducer 來實(shí)現(xiàn)從 HBase 當(dāng)中讀取與寫入數(shù)據(jù)。
這里我們將 myuser 這張表當(dāng)中 f1 列族的 name 和 age 字段寫入到 myuser2 這張表的 f1 列族當(dāng)中去。
需求一:讀取 myuser 這張表當(dāng)中的數(shù)據(jù)寫入到 HBase 的另外一張表當(dāng)中去:
第一步:創(chuàng)建 myuser2 這張表
注意:列族的名字要與 myuser 表的列族名字相同
hbase(main):010:0> create 'myuser2','f1'
第二步:開發(fā) MR 的程序
第三步:打包運(yùn)行
將我們打好的 jar 包放到服務(wù)器上執(zhí)行:
yarn jar hbaseStudy-1.0-SNAPSHOT.jar cn.yuan_more.hbasemr.HBaseMR
需求二:讀取 HDFS 文件,寫入到 HBase 表當(dāng)中去
第一步:準(zhǔn)備數(shù)據(jù)文件
準(zhǔn)備數(shù)據(jù)文件,并將數(shù)據(jù)文件上傳到 HDFS 上面去。
第二步:開發(fā) MR 程序
需求四:通過 bulkload 的方式批量加載數(shù)據(jù)到 HBase 當(dāng)中去
加載數(shù)據(jù)到 HBase 當(dāng)中去的方式多種多樣,我們可以使用 HBase 的 javaAPI 或者使用 sqoop 將我們的數(shù)據(jù)寫入或者導(dǎo)入到 HBase 當(dāng)中去,但是這些方式不是慢就是在導(dǎo)入的過程的占用 Region 資料導(dǎo)致效率低下,我們也可以通過 MR 的程序,將我們的數(shù)據(jù)直接轉(zhuǎn)換成 HBase 的最終存儲格式 HFile,然后直接 load 數(shù)據(jù)到 HBase 當(dāng)中去即可。
HBase 中每張 Table 在根目錄(/HBase)下用一個(gè)文件夾存儲,Table 名為文件夾名,在 Table 文件夾下每個(gè) Region 同樣用一個(gè)文件夾存儲,每個(gè) Region 文件夾下的每個(gè)列族也用文件夾存儲,而每個(gè)列族下存儲的就是一些 HFile 文件,HFile 就是 HBase 數(shù)據(jù)在 HFDS 下存儲格式,所以 HBase 存儲文件最終在 hdfs 上面的表現(xiàn)形式就是 HFile,如果我們可以直接將數(shù)據(jù)轉(zhuǎn)換為 HFile 的格式,那么我們的 HBase 就可以直接讀取加載 HFile 格式的文件,就可以直接讀取了。
優(yōu)點(diǎn):
導(dǎo)入過程不占用 Region 資源
能快速導(dǎo)入海量的數(shù)據(jù)
節(jié)省內(nèi)存
第一步:定義 mapper 類
第三步:將代碼打成 jar 包然后運(yùn)行
yarn jar original-h(huán)baseStudy-1.0-SNAPSHOT.jar cn.yuan_more.hbasemr.HBaseLoad
第四步:開發(fā)代碼,加載數(shù)據(jù)
將輸出路徑下面的 HFile 文件,加載到 hbase 表當(dāng)中去
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_h(yuǎn)file"), admin,table,connection.getRegionLocator(TableName.valueOf("myuser2")));
}
}
或者我們也可以通過命令行來進(jìn)行加載數(shù)據(jù)。
先將 hbase 的 jar 包添加到 hadoop 的 classpath 路徑下
export HADOOP_CLASSPATH=`${HBASE_HOME}/bin/hbase mapredcp`
然后執(zhí)行以下命令,將 hbase 的 HFile 直接導(dǎo)入到表 myuser2 當(dāng)中來
yarn jar /servers/hbase/lib/hbase-server-1.2.0.jar completebulkload /hbase/output_h(yuǎn)file myuser2
八、HBase 的預(yù)分區(qū)1. 為何要預(yù)分區(qū)?增加數(shù)據(jù)讀寫效率負(fù)載均衡,防止數(shù)據(jù)傾斜方便集群容災(zāi)調(diào)度 region優(yōu)化 Map 數(shù)量2. 如何預(yù)分區(qū)?
每一個(gè) region 維護(hù)著 startRow 與 endRowKey,如果加入的數(shù)據(jù)符合某個(gè) region 維護(hù)的 rowKey 范圍,則該數(shù)據(jù)交給這個(gè) region 維護(hù)。
3. 如何設(shè)定預(yù)分區(qū)?1) 手動(dòng)指定預(yù)分區(qū)hbase(main):001:0> create 'staff','info','partition1',SPLITS => ['1000','2000','3000','4000']
完成后如圖:
2) 使用 16 進(jìn)制算法生成預(yù)分區(qū)hbase(main):003:0> create 'staff2','info','partition2',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
完成后如圖:
3) 分區(qū)規(guī)則創(chuàng)建于文件中
創(chuàng)建 splits.txt 文件內(nèi)容如下:
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)建預(yù)分區(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();
//自定義算法,產(chǎn)生一系列Hash散列值存儲在二維數(shù)組中
byte[][] splitKeys = {{1,2,3,4,5},{'a','b','c','d','e'}};
//通過HTableDescriptor來實(shí)現(xiàn)我們表的參數(shù)設(shè)置,包括表名,列族等等
HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf("stuff4"));
//添加列族
hTableDescriptor.a(chǎn)ddFamily(new HColumnDescriptor("f1"));
//添加列族
hTableDescriptor.a(chǎn)ddFamily(new HColumnDescriptor("f2"));
admin.createTable(hTableDescriptor,splitKeys);
admin.close();
}
九、HBase 的 rowKey 設(shè)計(jì)技巧
HBase 是三維有序存儲的,通過 rowkey(行鍵),column key(column family 和 qualifier)和 TimeStamp(時(shí)間戳)這個(gè)三個(gè)維度可以對 HBase 中的數(shù)據(jù)進(jìn)行快速定位。
HBase 中 rowkey 可以唯一標(biāo)識一行記錄,在 HBase 查詢的時(shí)候,有以下幾種方式:
通過 get 方式,指定 rowkey 獲取唯一一條記錄;通過 scan 方式,設(shè)置 startRow 和 stopRow 參數(shù)進(jìn)行范圍匹配;全表掃描,即直接掃描整張表中所有行記錄。1. rowkey 長度原則
rowkey 是一個(gè)二進(jìn)制碼流,可以是任意字符串,最大長度 64kb,實(shí)際應(yīng)用中一般為 10-100bytes,以 byte[]形式保存,一般設(shè)計(jì)成定長。
建議越短越好,不要超過 16 個(gè)字節(jié),原因如下:
數(shù)據(jù)的持久化文件 HFile 中是按照 KeyValue 存儲的,如果 rowkey 過長,比如超過 100 字節(jié),1000w 行數(shù)據(jù),光 rowkey 就要占用 100*1000w=10 億個(gè)字節(jié),將近 1G 數(shù)據(jù),這樣會極大影響 HFile 的存儲效率;
MemStore 將緩存部分?jǐn)?shù)據(jù)到內(nèi)存,如果 rowkey 字段過長,內(nèi)存的有效利用率就會降低,系統(tǒng)不能緩存更多的數(shù)據(jù),這樣會降低檢索效率。
2. rowkey 散列原則
如果 rowkey 按照時(shí)間戳的方式遞增,不要將時(shí)間放在二進(jìn)制碼的前面,建議將 rowkey 的高位作為散列字段,由程序隨機(jī)生成,低位放時(shí)間字段,這樣將提高數(shù)據(jù)均衡分布在每個(gè) RegionServer,以實(shí)現(xiàn)負(fù)載均衡的幾率。
如果沒有散列字段,首字段直接是時(shí)間信息,所有的數(shù)據(jù)都會集中在一個(gè) RegionServer 上,這樣在數(shù)據(jù)檢索的時(shí)候負(fù)載會集中在個(gè)別的 RegionServer 上,造成熱點(diǎn)問題,會降低查詢效率。
3. rowkey 唯一原則
必須在設(shè)計(jì)上保證其唯一性,rowkey 是按照字典順序排序存儲的,因此,設(shè)計(jì) rowkey 的時(shí)候,要充分利用這個(gè)排序的特點(diǎn),將經(jīng)常讀取的數(shù)據(jù)存儲到一塊,將最近可能會被訪問的數(shù)據(jù)放到一塊。
4. 什么是熱點(diǎn)
HBase 中的行是按照 rowkey 的字典順序排序的,這種設(shè)計(jì)優(yōu)化了 scan 操作,可以將相關(guān)的行以及會被一起讀取的行存取在臨近位置,便于 scan。然而糟糕的 rowkey 設(shè)計(jì)是熱點(diǎn)的源頭。
熱點(diǎn)發(fā)生在大量的 client 直接訪問集群的一個(gè)或極少數(shù)個(gè)節(jié)點(diǎn)(訪問可能是讀,寫或者其他操作)。大量訪問會使熱點(diǎn) region 所在的單個(gè)機(jī)器超出自身承受能力,引起性能下降甚至 region 不可用,這也會影響同一個(gè) RegionServer 上的其他 region,由于主機(jī)無法服務(wù)其他 region 的請求。
設(shè)計(jì)良好的數(shù)據(jù)訪問模式以使集群被充分,均衡的利用。為了避免寫熱點(diǎn),設(shè)計(jì) rowkey 使得不同行在同一個(gè) region,但是在更多數(shù)據(jù)情況下,數(shù)據(jù)應(yīng)該被寫入集群的多個(gè) region,而不是一個(gè)。
下面是一些常見的避免熱點(diǎn)的方法以及它們的優(yōu)缺點(diǎn):
1) 加鹽
這里所說的加鹽不是密碼學(xué)中的加鹽,而是在 rowkey 的前面增加隨機(jī)數(shù),具體就是給 rowkey 分配一個(gè)隨機(jī)前綴以使得它和之前的 rowkey 的開頭不同。分配的前綴種類數(shù)量應(yīng)該和你想使用數(shù)據(jù)分散到不同的 region 的數(shù)量一致。加鹽之后的 rowkey 就會根據(jù)隨機(jī)生成的前綴分散到各個(gè) region 上,以避免熱點(diǎn)。
2) 哈希
哈希會使同一行永遠(yuǎn)用一個(gè)前綴加鹽。哈希也可以使負(fù)載分散到整個(gè)集群,但是讀卻是可以預(yù)測的。使用確定的哈?梢宰尶蛻舳酥貥(gòu)完整的 rowkey,可以使用 get 操作準(zhǔn)確獲取某一個(gè)行數(shù)據(jù)。
3) 反轉(zhuǎn)
第三種防止熱點(diǎn)的方法時(shí)反轉(zhuǎn)固定長度或者數(shù)字格式的 rowkey。這樣可以使得 rowkey 中經(jīng)常改變的部分(最沒有意義的部分)放在前面。這樣可以有效的隨機(jī) rowkey,但是犧牲了 rowkey 的有序性。
反轉(zhuǎn) rowkey 的例子以手機(jī)號為 rowkey,可以將手機(jī)號反轉(zhuǎn)后的字符串作為 rowkey,這樣的就避免了以手機(jī)號那樣比較固定開頭導(dǎo)致熱點(diǎn)問題。
3) 時(shí)間戳反轉(zhuǎn)
一個(gè)常見的數(shù)據(jù)處理問題是快速獲取數(shù)據(jù)的最近版本,使用反轉(zhuǎn)的時(shí)間戳作為 rowkey 的一部分對這個(gè)問題十分有用,可以用 Long.Max_Value - timestamp 追加到 key 的末尾,例如 [key][reverse_timestamp] , [key] 的最新值可以通過 scan [key]獲得[key]的第一條記錄,因?yàn)?HBase 中 rowkey 是有序的,第一條記錄是最后錄入的數(shù)據(jù)。
其他一些建議:
盡量減少行鍵和列族的大小在 HBase 中,value 永遠(yuǎn)和它的 key 一起傳輸?shù)。?dāng)具體的值在系統(tǒng)間傳輸時(shí),它的 rowkey,列名,時(shí)間戳也會一起傳輸。如果你的 rowkey 和列名很大,這個(gè)時(shí)候它們將會占用大量的存儲空間。
列族盡可能越短越好,最好是一個(gè)字符。
冗長的屬性名雖然可讀性好,但是更短的屬性名存儲在 HBase 中會更好。
十、HBase 的協(xié)處理器
http://hbase.a(chǎn)pache.org/book.html#cp
1. 起源
Hbase 作為列族數(shù)據(jù)庫最經(jīng)常被人詬病的特性包括:無法輕易建立“二級索引”,難以執(zhí)行求和、計(jì)數(shù)、排序等操作。
比如,在舊版本的(<0.92)Hbase 中,統(tǒng)計(jì)數(shù)據(jù)表的總行數(shù),需要使用 Counter 方法,執(zhí)行一次 MapReduce Job 才能得到。
雖然 HBase 在數(shù)據(jù)存儲層中集成了 MapReduce,能夠有效用于數(shù)據(jù)表的分布式計(jì)算。然而在很多情況下,做一些簡單的相加或者聚合計(jì)算的時(shí)候, 如果直接將計(jì)算過程放置在 server 端,能夠減少通訊開銷,從而獲得很好的性能提升。于是, HBase 在 0.92 之后引入了協(xié)處理器(coprocessors),實(shí)現(xiàn)一些激動(dòng)人心的新特性:能夠輕易建立二次索引、復(fù)雜過濾器(謂詞下推)以及訪問控制等。
2. 協(xié)處理器有兩種:observer 和 endpoint1) observer 協(xié)處理器
Observer 類似于傳統(tǒng)數(shù)據(jù)庫中的觸發(fā)器,當(dāng)發(fā)生某些事件的時(shí)候這類協(xié)處理器會被 Server 端調(diào)用。
Observer Coprocessor 就是一些散布在 HBase Server 端代碼中的 hook 鉤子,在固定的事件發(fā)生時(shí)被調(diào)用。
比如:put 操作之前有鉤子函數(shù) prePut,該函數(shù)在 put 操作執(zhí)行前會被 Region Server 調(diào)用;在 put 操作之后則有 postPut 鉤子函數(shù)。
以 HBase0.92 版本為例,它提供了三種觀察者接口:
RegionObserver:提供客戶端的數(shù)據(jù)操縱事件鉤子:Get、 Put、 Delete、 Scan 等。WALObserver:提供 WAL 相關(guān)操作鉤子。MasterObserver:提供 DDL-類型的操作鉤子。如創(chuàng)建、刪除、修改數(shù)據(jù)表等。
到 0.96 版本又新增一個(gè) RegionServerObserver
下圖是以 RegionObserver 為例子講解 Observer 這種協(xié)處理器的原理:
2) endpoint 協(xié)處理器
Endpoint 協(xié)處理器類似傳統(tǒng)數(shù)據(jù)庫中的存儲過程,客戶端可以調(diào)用這些 Endpoint 協(xié)處理器執(zhí)行一段 Server 端代碼,并將 Server 端代碼的結(jié)果返回給客戶端進(jìn)一步處理,最常見的用法就是進(jìn)行聚集操作。
如果沒有協(xié)處理器,當(dāng)用戶需要找出一張表中的最大數(shù)據(jù),即 max 聚合操作,就必須進(jìn)行全表掃描,在客戶端代碼內(nèi)遍歷掃描結(jié)果,并執(zhí)行求最大值的操作。這樣的方法無法利用底層集群的并發(fā)能力,而將所有計(jì)算都集中到 Client 端統(tǒng)一執(zhí)行,勢必效率低下。
利用 Coprocessor,用戶可以將求最大值的代碼部署到 HBase Server 端,HBase 將利用底層 cluster 的多個(gè)節(jié)點(diǎn)并發(fā)執(zhí)行求最大值的操作。即在每個(gè) Region 范圍內(nèi) 執(zhí)行求最大值的代碼,將每個(gè) Region 的最大值在 Region Server 端計(jì)算出,僅僅將該 max 值返回給客戶端。在客戶端進(jìn)一步將多個(gè) Region 的最大值進(jìn)一步處理而找到其中的最大值。這樣整體的執(zhí)行效率就會提高很多。
下圖是 EndPoint 的工作原理:
3. 協(xié)處理器加載方式
協(xié)處理器的加載方式有兩種,我們稱之為靜態(tài)加載方式( Static Load)和動(dòng)態(tài)加載方式( Dynamic Load)。
靜態(tài)加載的協(xié)處理器稱之為 System Coprocessor
動(dòng)態(tài)加載的協(xié)處理器稱之為 Table Coprocessor。
1) 靜態(tài)加載
通過修改 hbase-site.xml 這個(gè)文件來實(shí)現(xiàn), 啟動(dòng)全局 aggregation,能過操縱所有的表上的數(shù)據(jù)。只需要添加如下代碼:
<property>
<name>hbase.coprocessor.user.region.classes</name>
<value>org.a(chǎn)pache.hadoop.hbase.coprocessor.AggregateImplementation</value>
</property>
2) 動(dòng)態(tài)加載
啟用表 aggregation,只對特定的表生效。通過 HBase Shell 來實(shí)現(xiàn)。
disable 指定表
hbase> disable 'mytable'
添加 aggregation
hbase> alter 'mytable', METHOD => 'table_att','coprocessor'=>
'|org.a(chǎn)pache.Hadoop.hbase.coprocessor.AggregateImplementation||'
重啟指定表
hbase> enable 'mytable'
協(xié)處理器卸載
disable 'mytable'
alter 'mytable', METHOD => 'table_att_unset',NAME=>'coprocessor$1'
enable 'test'
十一、HBase當(dāng)中的二級索引的簡要介紹
由于HBase的查詢比較弱,如果需要實(shí)現(xiàn)類似于 select name,salary,count(1),max(salary) from user group by name,salary order by salary 等這樣的復(fù)雜性的統(tǒng)計(jì)需求,基本上不可能,或者說比較困難,所以我們在使用HBase的時(shí)候,一般都會借助二級索引的方案來進(jìn)行實(shí)現(xiàn)。
HBase的一級索引就是rowkey,我們只能通過rowkey進(jìn)行檢索。如果我們相對hbase里面列族的列列進(jìn)行一些組合查詢,就需要采用HBase的二級索引方案來進(jìn)行多條件的查詢。
1. MapReduce方案 2. ITHBASE(Indexed-Transanctional HBase)方案 3. IHBASE(Index HBase)方案 4. Hbase Coprocessor(協(xié)處理器)方案 5. Solr+hbase方案6. CCIndex(complementalclustering index)方案
常見的二級索引我們一般可以借助各種其他的方式來實(shí)現(xiàn),例如Phoenix或者solr或者ES等。
十二、HBase 調(diào)優(yōu)1. 通用優(yōu)化
NameNode的元數(shù)據(jù)備份使用SSD。
定時(shí)備份NameNode上的元數(shù)據(jù),每小時(shí)或者每天備份,如果數(shù)據(jù)極其重要,可以5~10分鐘備份一次。備份可以通過定時(shí)任務(wù)復(fù)制元數(shù)據(jù)目錄即可。
為NameNode指定多個(gè)元數(shù)據(jù)目錄,使用dfs.name.dir或者dfs.namenode.name.dir指定。一個(gè)指定本地磁盤,一個(gè)指定網(wǎng)絡(luò)磁盤。這樣可以提供元數(shù)據(jù)的冗余和健壯性,以免發(fā)生故障。
設(shè)置dfs.namenode.name.dir.restore為true,允許嘗試恢復(fù)之前失敗的dfs.namenode.name.dir目錄,在創(chuàng)建checkpoint時(shí)做此嘗試,如果設(shè)置了多個(gè)磁盤,建議允許。
NameNode節(jié)點(diǎn)必須配置為RAID1(鏡像盤)結(jié)構(gòu)。
保持NameNode日志目錄有足夠的空間,這些日志有助于幫助你發(fā)現(xiàn)問題。
因?yàn)镠adoop是IO密集型框架,所以盡量提升存儲的速度和吞吐量(類似位寬)。
2. Linux優(yōu)化開啟文件系統(tǒng)的預(yù)讀緩存可以提高讀取速度
$ sudo blockdev --setra 32768 /dev/sda
提示:ra是readahead的縮寫
關(guān)閉進(jìn)程睡眠池
$ sudo sysctl -w vm.swappiness=0
調(diào)整ulimit上限,默認(rèn)值為比較小的數(shù)字
$ ulimit -n 查看允許最大進(jì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 修改用戶打開進(jìn)程數(shù)限制
修改為:
#* soft nproc 4096
#root soft nproc unlimited
* soft nproc 40960
root soft nproc unlimited
開啟集群的時(shí)間同步NTP。
更新系統(tǒng)補(bǔ)丁(提示:更新補(bǔ)丁前,請先測試新版本補(bǔ)丁對集群節(jié)點(diǎn)的兼容性)
3. HDFS優(yōu)化(hdfs-site.xml)保證RPC調(diào)用會有較多的線程數(shù)
屬性:dfs.namenode.handler.count
解釋:該屬性是NameNode服務(wù)默認(rèn)線程數(shù),的默認(rèn)值是10,根據(jù)機(jī)器的可用內(nèi)存可以調(diào)整為50~100
屬性:dfs.datanode.handler.count
解釋:該屬性默認(rèn)值為10,是DataNode的處理線程數(shù),如果HDFS客戶端程序讀寫請求比較多,可以調(diào)高到15~20,設(shè)置的值越大,內(nèi)存消耗越多,不要調(diào)整的過高,一般業(yè)務(wù)中,5~10即可。
副本數(shù)的調(diào)整
屬性:dfs.replication
解釋:如果數(shù)據(jù)量巨大,且不是非常之重要,可以調(diào)整為2~3,如果數(shù)據(jù)非常之重要,可以調(diào)整為3~5。
文件塊大小的調(diào)整
屬性:dfs.blocksize
解釋:塊大小定義,該屬性應(yīng)該根據(jù)存儲的大量的單個(gè)文件大小來設(shè)置,如果大量的單個(gè)文件都小于100M,建議設(shè)置成64M塊大小,對于大于100M或者達(dá)到GB的這種情況,建議設(shè)置成256M,一般設(shè)置范圍波動(dòng)在64M~256M之間。
4. MapReduce優(yōu)化(mapred-site.xml)Job任務(wù)服務(wù)線程數(shù)調(diào)整
mapreduce.jobtracker.handler.count
該屬性是Job任務(wù)線程數(shù),默認(rèn)值是10,根據(jù)機(jī)器的可用內(nèi)存可以調(diào)整為50~100
Http服務(wù)器工作線程數(shù)
屬性:mapreduce.tasktracker.http.threads
解釋:定義HTTP服務(wù)器工作線程數(shù),默認(rèn)值為40,對于大集群可以調(diào)整到80~100
文件排序合并優(yōu)化
屬性:mapreduce.task.io.sort.factor
解釋:文件排序時(shí)同時(shí)合并的數(shù)據(jù)流的數(shù)量,這也定義了同時(shí)打開文件的個(gè)數(shù),默認(rèn)值為10,如果調(diào)高該參數(shù),可以明顯減少磁盤IO,即減少文件讀取的次數(shù)。
設(shè)置任務(wù)并發(fā)
屬性:mapreduce.map.speculative
解釋:該屬性可以設(shè)置任務(wù)是否可以并發(fā)執(zhí)行,如果任務(wù)多而小,該屬性設(shè)置為true可以明顯加快任務(wù)執(zhí)行效率,但是對于延遲非常高的任務(wù),建議改為false,這就類似于迅雷下載。
MR輸出數(shù)據(jù)的壓縮
屬性:mapreduce.map.output.compress、mapreduce.output.fileoutputformat.compress
解釋:對于大集群而言,建議設(shè)置Map-Reduce的輸出為壓縮的數(shù)據(jù),而對于小集群,則不需要。
優(yōu)化Mapper和Reducer的個(gè)數(shù)
屬性:
mapreduce.tasktracker.map.tasks.maximum
mapreduce.tasktracker.reduce.tasks.maximum
解釋:以上兩個(gè)屬性分別為一個(gè)單獨(dú)的Job任務(wù)可以同時(shí)運(yùn)行的Map和Reduce的數(shù)量。
設(shè)置上面兩個(gè)參數(shù)時(shí),需要考慮CPU核數(shù)、磁盤和內(nèi)存容量。假設(shè)一個(gè)8核的CPU,業(yè)務(wù)內(nèi)容非常消耗CPU,那么可以設(shè)置map數(shù)量
4,如果該業(yè)務(wù)不是特別消耗CPU類型的,那么可以設(shè)置map數(shù)量為40,reduce數(shù)量為20。這些參數(shù)的值修改完成之后,一定要觀察是否有較長等待的任務(wù),如果有的話,可以減少數(shù)量以加快任務(wù)執(zhí)行,如果設(shè)置一個(gè)很大的值,會引起大量的上下文切換,以及內(nèi)存與磁盤之間的數(shù)據(jù)交換,這里沒有標(biāo)準(zhǔn)的配置數(shù)值,需要根據(jù)業(yè)務(wù)和硬件配置以及經(jīng)驗(yàn)來做出選擇。
在同一時(shí)刻,不要同時(shí)運(yùn)行太多的MapReduce,這樣會消耗過多的內(nèi)存,任務(wù)會執(zhí)行的非常緩慢,我們需要根據(jù)CPU核數(shù),內(nèi)存容量設(shè)置一個(gè)MR任務(wù)并發(fā)的最大值,使固定數(shù)據(jù)量的任務(wù)完全加載到內(nèi)存中,避免頻繁的內(nèi)存和磁盤數(shù)據(jù)交換,從而降低磁盤IO,提高性能。
大概估算公式:map = 2 + 2/3cpu_core
reduce = 2 + 1/3cpu_core
5. HBase優(yōu)化在HDFS的文件中追加內(nèi)容
HDFS 不是不允許追加內(nèi)容么?沒錯(cuò),請看背景故事:
屬性:dfs.support.a(chǎn)ppend
文件:hdfs-site.xml、hbase-site.xml
解釋:開啟HDFS追加同步,可以優(yōu)秀的配合HBase的數(shù)據(jù)同步和持久化。默認(rèn)值為true。
優(yōu)化DataNode允許的最大文件打開數(shù)
屬性:dfs.datanode.max.transfer.threads
文件:hdfs-site.xml
解釋:HBase一般都會同一時(shí)間操作大量的文件,根據(jù)集群的數(shù)量和規(guī)模以及數(shù)據(jù)動(dòng)作,設(shè)置為4096或者更高。默認(rèn)值:4096
**優(yōu)化延遲高的數(shù)據(jù)操作的等待時(shí)間
屬性:dfs.image.transfer.timeout
文件:hdfs-site.xml
解釋:如果對于某一次數(shù)據(jù)操作來講,延遲非常高,socket需要等待更長的時(shí)間,建議把該值設(shè)置為更大的值(默認(rèn)60000毫秒),以確保socket不會被timeout掉。
優(yōu)化數(shù)據(jù)的寫入效率
屬性:
mapreduce.map.output.compress
mapreduce.map.output.compress.codec
文件:mapred-site.xml
解釋:開啟這兩個(gè)數(shù)據(jù)可以大大提高文件的寫入效率,減少寫入時(shí)間。第一個(gè)屬性值修改為true,第二個(gè)屬性值修改為:org.a(chǎn)pache.hadoop.io.compress.GzipCodec
優(yōu)化DataNode存儲
屬性:dfs.datanode.failed.volumes.tolerated
文件:hdfs-site.xml
解釋:默認(rèn)為0,意思是當(dāng)DataNode中有一個(gè)磁盤出現(xiàn)故障,則會認(rèn)為該DataNode shutdown了。如果修改為1,則一個(gè)磁盤出現(xiàn)故障時(shí),數(shù)據(jù)會被復(fù)制到其他正常的DataNode上,當(dāng)前的DataNode繼續(xù)工作。
設(shè)置RPC監(jiān)聽數(shù)量
屬性:hbase.regionserver.handler.count
文件:hbase-site.xml
解釋:默認(rèn)值為30,用于指定RPC監(jiān)聽的數(shù)量,可以根據(jù)客戶端的請求數(shù)進(jìn)行調(diào)整,讀寫請求較多時(shí),增加此值。
優(yōu)化HStore文件大小
屬性:hbase.hregion.max.filesize
文件:hbase-site.xml
解釋:默認(rèn)值10737418240(10GB),如果需要運(yùn)行HBase的MR任務(wù),可以減小此值,因?yàn)橐粋(gè)region對應(yīng)一個(gè)map任務(wù),如果單個(gè)region過大,會導(dǎo)致map任務(wù)執(zhí)行時(shí)間過長。該值的意思就是,如果HFile的大小達(dá)到這個(gè)數(shù)值,則這個(gè)region會被切分為兩個(gè)Hfile。
優(yōu)化hbase客戶端緩存
屬性:hbase.client.write.buffer
文件:hbase-site.xml
解釋:用于指定HBase客戶端緩存,增大該值可以減少RPC調(diào)用次數(shù),但是會消耗更多內(nèi)存,反之則反之。一般我們需要設(shè)定一定的緩存大小,以達(dá)到減少RPC次數(shù)的目的。
指定scan.next掃描HBase所獲取的行數(shù)
屬性:hbase.client.scanner.caching
文件:hbase-site.xml
解釋:用于指定scan.next方法獲取的默認(rèn)行數(shù),值越大,消耗內(nèi)存越大。
6. 內(nèi)存優(yōu)化
HBase操作過程中需要大量的內(nèi)存開銷,畢竟Table是可以緩存在內(nèi)存中的,一般會分配整個(gè)可用內(nèi)存的70%給HBase的Java堆。但是不建議分配非常大的堆內(nèi)存,因?yàn)镚C過程持續(xù)太久會導(dǎo)致RegionServer處于長期不可用狀態(tài),一般16~48G內(nèi)存就可以了,如果因?yàn)榭蚣苷加脙?nèi)存過高導(dǎo)致系統(tǒng)內(nèi)存不足,框架一樣會被系統(tǒng)服務(wù)拖死。
7. JVM優(yōu)化
涉及文件:hbase-env.sh
并行GC
參數(shù):·-XX:+UseParallelGC·
解釋:開啟并行GC
同時(shí)處理垃圾回收的線程數(shù)
參數(shù):-XX:ParallelGCThreads=cpu_core – 1
解釋:該屬性設(shè)置了同時(shí)處理垃圾回收的線程數(shù)。
禁用手動(dòng)GC
參數(shù):-XX:DisableExplicitGC
解釋:防止開發(fā)人員手動(dòng)調(diào)用GC
8. Zookeeper優(yōu)化優(yōu)化Zookeeper會話超時(shí)時(shí)間
參數(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)。
該值會直接關(guān)系到master發(fā)現(xiàn)服務(wù)器宕機(jī)的最大周期,默認(rèn)值為30秒,如果該值過小,會在HBase在寫入大量數(shù)據(jù)發(fā)生而GC時(shí),導(dǎo)致RegionServer短暫的不可用,從而沒有向ZK發(fā)送心跳包,最終導(dǎo)致認(rèn)為從節(jié)點(diǎn)shutdown。一般20臺左右的集群需要配置5臺zookeeper。
十三、HBase 大廠面試題解析1. Hbase是怎么寫數(shù)據(jù)的?
Client寫入 -> 存入MemStore,一直到MemStore滿 -> Flush成一個(gè)StoreFile,直至增長到一定閾值 -> 觸發(fā)Compact合并操作 -> 多個(gè)StoreFile合并成一個(gè)StoreFile,同時(shí)進(jìn)行版本合并和數(shù)據(jù)刪除 -> 當(dāng)StoreFiles Compact后,逐步形成越來越大的StoreFile -> 單個(gè)StoreFile大小超過一定閾值后(默認(rèn)10G),觸發(fā)Split操作,把當(dāng)前Region Split成2個(gè)Region,Region會下線,新Split出的2個(gè)孩子Region會被HMaster分配到相應(yīng)的HRegionServer 上,使得原先1個(gè)Region的壓力得以分流到2個(gè)Region上
由此過程可知,HBase只是增加數(shù)據(jù),沒有更新和刪除操作,用戶的更新和刪除都是邏輯層面的,在物理層面,更新只是追加操作,刪除只是標(biāo)記操作。
用戶寫操作只需要進(jìn)入到內(nèi)存即可立即返回,從而保證I/O高性能。
2. HDFS和HBase各自使用場景
首先一點(diǎn)需要明白:Hbase是基于HDFS來存儲的。
HDFS:
一次性寫入,多次讀取。
保證數(shù)據(jù)的一致性。
主要是可以部署在許多廉價(jià)機(jī)器中,通過多副本提高可靠性,提供了容錯(cuò)和恢復(fù)機(jī)制。
HBase:
瞬間寫入量很大,數(shù)據(jù)庫不好支撐或需要很高成本支撐的場景。
數(shù)據(jù)需要長久保存,且量會持久增長到比較大的場景。
HBase不適用與有 join,多級索引,表關(guān)系復(fù)雜的數(shù)據(jù)模型。
大數(shù)據(jù)量(100s TB級數(shù)據(jù))且有快速隨機(jī)訪問的需求。如:淘寶的交易歷史記錄。數(shù)據(jù)量巨大無容置疑,面向普通用戶的請求必然要即時(shí)響應(yīng)。
業(yè)務(wù)場景簡單,不需要關(guān)系數(shù)據(jù)庫中很多特性(例如交叉列、交叉表,事務(wù),連接等等)。
3. Hbase的存儲結(jié)構(gòu)
Hbase 中的每張表都通過行鍵(rowkey)按照一定的范圍被分割成多個(gè)子表(HRegion),默認(rèn)一個(gè)HRegion 超過256M 就要被分割成兩個(gè),由HRegionServer管理,管理哪些 HRegion 由 Hmaster 分配。HRegion 存取一個(gè)子表時(shí),會創(chuàng)建一個(gè) HRegion 對象,然后對表的每個(gè)列族(Column Family)創(chuàng)建一個(gè) store 實(shí)例, 每個(gè) store 都會有 0 個(gè)或多個(gè) StoreFile 與之對應(yīng),每個(gè) StoreFile 都會對應(yīng)一個(gè)HFile,HFile 就是實(shí)際的存儲文件,一個(gè) HRegion 還擁有一個(gè) MemStore實(shí)例。
4. 熱點(diǎn)現(xiàn)象(數(shù)據(jù)傾斜)怎么產(chǎn)生的,以及解決方法有哪些
熱點(diǎn)現(xiàn)象:
某個(gè)小的時(shí)段內(nèi),對HBase的讀寫請求集中到極少數(shù)的Region上,導(dǎo)致這些region所在的RegionServer處理請求量驟增,負(fù)載量明顯偏大,而其他的RgionServer明顯空閑。
熱點(diǎn)現(xiàn)象出現(xiàn)的原因:
HBase中的行是按照rowkey的字典順序排序的,這種設(shè)計(jì)優(yōu)化了scan操作,可以將相關(guān)的行以及會被一起讀取的行存取在臨近位置,便于scan。然而糟糕的rowkey設(shè)計(jì)是熱點(diǎn)的源頭。
熱點(diǎn)發(fā)生在大量的client直接訪問集群的一個(gè)或極少數(shù)個(gè)節(jié)點(diǎn)(訪問可能是讀,寫或者其他操作)。大量訪問會使熱點(diǎn)region所在的單個(gè)機(jī)器超出自身承受能力,引起性能下降甚至region不可用,這也會影響同一個(gè)RegionServer上的其他region,由于主機(jī)無法服務(wù)其他region的請求。
熱點(diǎn)現(xiàn)象解決辦法:
為了避免寫熱點(diǎn),設(shè)計(jì)rowkey使得不同行在同一個(gè)region,但是在更多數(shù)據(jù)情況下,數(shù)據(jù)應(yīng)該被寫入集群的多個(gè)region,而不是一個(gè)。常見的方法有以下這些:
加鹽:在rowkey的前面增加隨機(jī)數(shù),使得它和之前的rowkey的開頭不同。分配的前綴種類數(shù)量應(yīng)該和你想使用數(shù)據(jù)分散到不同的region的數(shù)量一致。加鹽之后的rowkey就會根據(jù)隨機(jī)生成的前綴分散到各個(gè)region上,以避免熱點(diǎn)。
哈希:哈希可以使負(fù)載分散到整個(gè)集群,但是讀卻是可以預(yù)測的。使用確定的哈?梢宰尶蛻舳酥貥(gòu)完整的rowkey,可以使用get操作準(zhǔn)確獲取某一個(gè)行數(shù)據(jù)
反轉(zhuǎn):第三種防止熱點(diǎn)的方法時(shí)反轉(zhuǎn)固定長度或者數(shù)字格式的rowkey。這樣可以使得rowkey中經(jīng)常改變的部分(最沒有意義的部分)放在前面。這樣可以有效的隨機(jī)rowkey,但是犧牲了rowkey的有序性。反轉(zhuǎn)rowkey的例子以手機(jī)號為rowkey,可以將手機(jī)號反轉(zhuǎn)后的字符串作為rowkey,這樣的就避免了以手機(jī)號那樣比較固定開頭導(dǎo)致熱點(diǎn)問題
時(shí)間戳反轉(zhuǎn):一個(gè)常見的數(shù)據(jù)處理問題是快速獲取數(shù)據(jù)的最近版本,使用反轉(zhuǎn)的時(shí)間戳作為rowkey的一部分對這個(gè)問題十分有用,可以用 Long.Max_Value - timestamp 追加到key的末尾,例如[key][reverse_timestamp],[key]的最新值可以通過scan [key]獲得[key]的第一條記錄,因?yàn)镠Base中rowkey是有序的,第一條記錄是最后錄入的數(shù)據(jù)。
比如需要保存一個(gè)用戶的操作記錄,按照操作時(shí)間倒序排序,在設(shè)計(jì)rowkey的時(shí)候,可以這樣設(shè)計(jì)[userId反轉(zhuǎn)][Long.Max_Value - timestamp],在查詢用戶的所有操作記錄數(shù)據(jù)的時(shí)候,直接指定反轉(zhuǎn)后的userId,startRow是[userId反轉(zhuǎn)][000000000000],stopRow是[userId反轉(zhuǎn)][Long.Max_Value - timestamp]
如果需要查詢某段時(shí)間的操作記錄,startRow是[user反轉(zhuǎn)][Long.Max_Value - 起始時(shí)間],stopRow是[userId反轉(zhuǎn)][Long.Max_Value - 結(jié)束時(shí)間]
HBase建表預(yù)分區(qū):創(chuàng)建HBase表時(shí),就預(yù)先根據(jù)可能的RowKey劃分出多個(gè)region而不是默認(rèn)的一個(gè),從而可以將后續(xù)的讀寫操作負(fù)載均衡到不同的region上,避免熱點(diǎn)現(xiàn)象。
5. HBase的 rowkey 設(shè)計(jì)原則
長度原則:100字節(jié)以內(nèi),8的倍數(shù)最好,可能的情況下越短越好。因?yàn)镠File是按照 keyvalue 存儲的,過長的rowkey會影響存儲效率;其次,過長的rowkey在memstore中較大,影響緩沖效果,降低檢索效率。最后,操作系統(tǒng)大多為64位,8的倍數(shù),充分利用操作系統(tǒng)的最佳性能。
散列原則:高位散列,低位時(shí)間字段。避免熱點(diǎn)問題。
唯一原則:分利用這個(gè)排序的特點(diǎn),將經(jīng)常讀取的數(shù)據(jù)存儲到一塊,將最近可能會被訪問 的數(shù)據(jù)放到一塊。
6. HBase的列簇設(shè)計(jì)
原則:在合理范圍內(nèi)能盡量少的減少列簇就盡量減少列簇,因?yàn)榱写厥枪蚕韗egion的,每個(gè)列簇?cái)?shù)據(jù)相差太大導(dǎo)致查詢效率低下。
最優(yōu):將所有相關(guān)性很強(qiáng)的 key-value 都放在同一個(gè)列簇下,這樣既能做到查詢效率最高,也能保持盡可能少的訪問不同的磁盤文件。以用戶信息為例,可以將必須的基本信息存放在一個(gè)列族,而一些附加的額外信息可以放在另一列族。
7. HBase 中 compact 用途是什么,什么時(shí)候觸發(fā),分為哪兩種,有什么區(qū)別
在 hbase 中每當(dāng)有 memstore 數(shù)據(jù) flush 到磁盤之后,就形成一個(gè) storefile,當(dāng) storeFile的數(shù)量達(dá)到一定程度后,就需要將 storefile 文件來進(jìn)行 compaction 操作。
Compact 的作用:
合并文件
清除過期,多余版本的數(shù)據(jù)
提高讀寫數(shù)據(jù)的效率4HBase 中實(shí)現(xiàn)了兩種 compaction 的方式:minor and major. 這兩種 compaction 方式的區(qū)別是:
Minor 操作只用來做部分文件的合并操作以及包括 minVersion=0 并且設(shè)置 ttl 的過期版本清理,不做任何刪除數(shù)據(jù)、多版本數(shù)據(jù)的清理工作。
Major 操作是對 Region 下的 HStore 下的所有 StoreFile 執(zhí)行合并操作,最終的結(jié)果是整理合并出一個(gè)文件。

請輸入評論內(nèi)容...
請輸入評論/評論長度6~500個(gè)字
最新活動(dòng)更多
-
3月27日立即報(bào)名>> 【工程師系列】汽車電子技術(shù)在線大會
-
4月30日立即下載>> 【村田汽車】汽車E/E架構(gòu)革新中,新智能座艙挑戰(zhàn)的解決方案
-
5月15-17日立即預(yù)約>> 【線下巡回】2025年STM32峰會
-
即日-5.15立即報(bào)名>>> 【在線會議】安森美Hyperlux™ ID系列引領(lǐng)iToF技術(shù)革新
-
5月15日立即下載>> 【白皮書】精確和高效地表征3000V/20A功率器件應(yīng)用指南
-
5月16日立即參評 >> 【評選啟動(dòng)】維科杯·OFweek 2025(第十屆)人工智能行業(yè)年度評選
推薦專題
- 1 UALink規(guī)范發(fā)布:挑戰(zhàn)英偉達(dá)AI統(tǒng)治的開始
- 2 北電數(shù)智主辦酒仙橋論壇,探索AI產(chǎn)業(yè)發(fā)展新路徑
- 3 降薪、加班、裁員三重暴擊,“AI四小龍”已折戟兩家
- 4 “AI寒武紀(jì)”爆發(fā)至今,五類新物種登上歷史舞臺
- 5 國產(chǎn)智駕迎戰(zhàn)特斯拉FSD,AI含量差幾何?
- 6 光計(jì)算迎來商業(yè)化突破,但落地仍需時(shí)間
- 7 東陽光:2024年扭虧、一季度凈利大增,液冷疊加具身智能打開成長空間
- 8 地平線自動(dòng)駕駛方案解讀
- 9 封殺AI“照騙”,“淘寶們”終于不忍了?
- 10 優(yōu)必選:營收大增主靠小件,虧損繼續(xù)又逢關(guān)稅,能否乘機(jī)器人東風(fēng)翻身?