柚子快報(bào)邀請(qǐng)碼778899分享:HBase2.x學(xué)習(xí)筆記
柚子快報(bào)邀請(qǐng)碼778899分享:HBase2.x學(xué)習(xí)筆記
文章目錄
一、HBase 簡介1、HBase 定義1.1 概述1.2 HBase 與 Hadoop 的關(guān)系1.3 RDBMS 與 HBase 的對(duì)比1.4 HBase 特征簡要
2、HBase 數(shù)據(jù)模型2.1 HBase 邏輯結(jié)構(gòu)2.2 HBase 物理存儲(chǔ)結(jié)構(gòu)2.3 HBase的表數(shù)據(jù)模型
3、HBase 基本架構(gòu)3.1 Master3.2 Region Server3.3 Zookeeper3.4 HDFS
二、HBase 快速入門1、HBase 安裝部署1.1 前置環(huán)境與下載1.2 HBase 的配置文件1.3 分發(fā)與啟動(dòng)1.4 高可用(可選,推薦)
2、HBase Shell 基本操作2.1 基本操作2.2 DDL2.3 DML2.4 其他
三、HBase API1、環(huán)境準(zhǔn)備與連接1.1 環(huán)境準(zhǔn)備1.2 單線程連接1.3 多線程創(chuàng)建連接(推薦)
2、DDL2.1 創(chuàng)建命名空間2.2 判斷表格是否存在2.3 創(chuàng)建表2.4 修改表2.5 刪除表
3、DML3.1 插入數(shù)據(jù)3.2 讀取數(shù)據(jù)3.3 掃描數(shù)據(jù)3.4 帶過濾掃描3.5 刪除數(shù)據(jù)
四、HBase 原理分析1、架構(gòu)分析1.1 Master 架構(gòu)1.2 RegionServer 架構(gòu)
2、HRegion管理和HMaster工作機(jī)制2.1 HRegion管理2.2 HMaster工作機(jī)制
3、寫流程4、MemStore Flush5、讀流程5.1 HFile 結(jié)構(gòu)5.2 讀流程5.3 合并讀取數(shù)據(jù)優(yōu)化
6、StoreFile Compaction7、Region Split7.1 預(yù)分區(qū)(自定義分區(qū))7.2 系統(tǒng)拆分
五、HBase 優(yōu)化1、RowKey 設(shè)計(jì)1.1 實(shí)現(xiàn)需求 11.2 實(shí)現(xiàn)需求 21.3 添加預(yù)分區(qū)優(yōu)化
2、參數(shù)優(yōu)化2.1 Zookeeper 會(huì)話超時(shí)時(shí)間2.2 設(shè)置 RPC 監(jiān)聽數(shù)量2.3 手動(dòng)控制 Major Compaction2.4 優(yōu)化 HStore 文件大小2.5 優(yōu)化 HBase 客戶端緩存2.6 指定 scan.next 掃描 HBase 所獲取的行數(shù)2.7 BlockCache 占用 RegionServer 堆內(nèi)存的比例2.8 MemStore 占用 RegionServer 堆內(nèi)存的比例
3、JVM 調(diào)優(yōu)4、HBase 使用經(jīng)驗(yàn)法則
六、整合 Phoenix1、Phoenix 簡介1.1 Phoenix 定義1.2 為什么使用 Phoenix
2、Phoenix快速入門2.1 Phoenix安裝部署2.2 Phoenix Shell 操作2.3 Phoenix JDBC 操作
3、Phoenix 二級(jí)索引3.1 二級(jí)索引配置文件3.2 全局索引(global index)3.3 包含索引(covered index)3.4 本地索引(local index)
七、Hive集成1、集成使用1.1 使用場景1.2 配置
2、案例舉例2.1 案例一2.2 案例二
一、HBase 簡介
1、HBase 定義
官網(wǎng):https://hbase.apache.org/
1.1 概述
HBase 是 BigTable 的開源 Java 版本。是建立在 HDFS 之上,提供高可靠性、高性能、列存儲(chǔ)、可伸縮、實(shí)時(shí)讀寫 NoSql 的數(shù)據(jù)庫系統(tǒng)。它介于 NoSql 和 RDBMS 之間,僅能通過主鍵(row key)和主鍵的 range 來檢索數(shù)據(jù),僅支持單行事務(wù)(可通過 hive 支持來實(shí)現(xiàn)多表 join 等復(fù)雜操作)。
主要用來存儲(chǔ)結(jié)構(gòu)化和半結(jié)構(gòu)化的松散數(shù)據(jù)。Hbase 查詢數(shù)據(jù)功能很簡單,不支持 join 等復(fù)雜操作,不支持復(fù)雜的事務(wù)(行級(jí)的事務(wù)) Hbase 中支持的數(shù)據(jù)類型:byte[] 與 hadoop 一樣,Hbase 目標(biāo)主要依靠橫向擴(kuò)展,通過不斷增加廉價(jià)的商用服務(wù)器,來增加計(jì)算和存儲(chǔ)能力。HBase 中的表一般有這樣的特點(diǎn):
大:一個(gè)表可以有上十億行,上百萬列面向列:面向列(族)的存儲(chǔ)和權(quán)限控制,列(族)獨(dú)立檢索。稀疏:對(duì)于為空(null)的列,并不占用存儲(chǔ)空間,因此,表可以設(shè)計(jì)的非常稀疏
1.2 HBase 與 Hadoop 的關(guān)系
HDFS
為分布式存儲(chǔ)提供文件系統(tǒng)針對(duì)存儲(chǔ)大尺寸的文件進(jìn)行優(yōu)化,不需要對(duì) HDFS 上的文件進(jìn)行隨機(jī)讀寫直接使用文件數(shù)據(jù)模型不靈活使用文件系統(tǒng)和處理框架優(yōu)化一次寫入,多次讀取的方式
HBase
提供表狀的面向列的數(shù)據(jù)存儲(chǔ)針對(duì)表狀數(shù)據(jù)的隨機(jī)讀寫進(jìn)行優(yōu)化使用 key-value 操作數(shù)據(jù)提供靈活的數(shù)據(jù)模型使用表狀存儲(chǔ),支持 MapReduce,依賴 HDFS優(yōu)化了多次讀,以及多次寫
1.3 RDBMS 與 HBase 的對(duì)比
關(guān)系型數(shù)據(jù)庫
結(jié)構(gòu):
數(shù)據(jù)庫以表的形式存在支持 FAT、NTFS、EXT、文件系統(tǒng)使用 Commit log 存儲(chǔ)日志參考系統(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)存儲(chǔ)日志參考系統(tǒng)是 Zookeeper使用行鍵(row key)支持分片使用行、列、列族和單元格
功能:
支持向外擴(kuò)展使用 API 和 MapReduce 來訪問 HBase 表數(shù)據(jù)面向列,即每一列都是一個(gè)連續(xù)的單元數(shù)據(jù)總量不依賴具體某臺(tái)機(jī)器,而取決于機(jī)器數(shù)量HBase 不支持 ACID(Atomicity、Consistency、Isolation、Durability)適合結(jié)構(gòu)化數(shù)據(jù)和非結(jié)構(gòu)化數(shù)據(jù)一般都是分布式的HBase 不支持事務(wù)不支持 Join
1.4 HBase 特征簡要
海量存儲(chǔ)
Hbase 適合存儲(chǔ) PB 級(jí)別的海量數(shù)據(jù),在 PB 級(jí)別的數(shù)據(jù)以及采用廉價(jià) PC 存儲(chǔ)的情況下,能在幾十到百毫秒內(nèi)返回?cái)?shù)據(jù)。這與 Hbase 的極易擴(kuò)展性息息相關(guān)。正式因?yàn)?Hbase 良好的擴(kuò)展性,才為海量數(shù)據(jù)的存儲(chǔ)提供了便利。
列式存儲(chǔ)
這里的列式存儲(chǔ)其實(shí)說的是列族存儲(chǔ),Hbase 是根據(jù)列族來存儲(chǔ)數(shù)據(jù)的。列族下面可以有非常多的列,列族在創(chuàng)建表的時(shí)候就必須指定
極易擴(kuò)展
Hbase 的擴(kuò)展性主要體現(xiàn)在兩個(gè)方面,一個(gè)是基于上層處理能力(RegionServer)的擴(kuò)展,一個(gè)是基于存儲(chǔ)的擴(kuò)展(HDFS)。 通過橫向添加 RegionSever 的機(jī)器,進(jìn)行水平擴(kuò)展,提升 Hbase 上層的處理能力,提升 Hbsae 服務(wù)更多 Region 的能力。 備注:RegionServer 的作用是管理 region、承接業(yè)務(wù)的訪問,這個(gè)后面會(huì)詳細(xì)的介紹通過橫向添加 Datanode 的機(jī)器,進(jìn)行存儲(chǔ)層擴(kuò)容,提升 Hbase 的數(shù)據(jù)存儲(chǔ)能力和提升后端存儲(chǔ)的讀寫能力
高并發(fā)
由于目前大部分使用 Hbase 的架構(gòu),都是采用的廉價(jià) PC,因此單個(gè) IO 的延遲其實(shí)并不小,一般在幾十到上百 ms 之間。這里說的高并發(fā),主要是在并發(fā)的情況下,Hbase 的單個(gè) IO 延遲下降并不多。能獲得高并發(fā)、低延遲的服務(wù)
稀疏
稀疏主要是針對(duì) Hbase 列的靈活性,在列族中,你可以指定任意多的列,在列數(shù)據(jù)為空的情況下,是不會(huì)占用存儲(chǔ)空間的
2、HBase 數(shù)據(jù)模型
HBase 的設(shè)計(jì)理念依據(jù) Google 的 BigTable 論文,論文中對(duì)于數(shù)據(jù)模型的首句介紹:Bigtable 是一個(gè)稀疏的、分布式的、持久的多維排序 map。之后對(duì)于映射的解釋如下:該映射由行鍵、列鍵和時(shí)間戳索引;映射中的每個(gè)值都是一個(gè)未解釋的字節(jié)數(shù)組。最終 HBase 關(guān)于數(shù)據(jù)模型和 BigTable 的對(duì)應(yīng)關(guān)系如下:HBase 使用與 Bigtable 非常相似的數(shù)據(jù)模型。用戶將數(shù)據(jù)行存儲(chǔ)在帶標(biāo)簽的表中。數(shù)據(jù)行具有可排序的鍵和任意數(shù)量的列。該表存儲(chǔ)稀疏,因此如果用戶喜歡,同一表中的行可以具有瘋狂變化的列。
最終理解 HBase 數(shù)據(jù)模型的關(guān)鍵在于稀疏、分布式、多維、排序的映射。其中映射 map指代非關(guān)系型數(shù)據(jù)庫的 key-Value 結(jié)構(gòu)
2.1 HBase 邏輯結(jié)構(gòu)
HBase 可以用于存儲(chǔ)多種結(jié)構(gòu)的數(shù)據(jù),以 JSON 為例,存儲(chǔ)的數(shù)據(jù)原貌為
{
"row_key1": {
"personal_info": {
"name": "zhangsan",
"city": " 北 京 ",
"phone": "131********"
},
"office_info": {
"tel": "010-1111111",
"address": "atguigu"
}
},
"row_key11": {
"personal_info": {
"city": " 上 海 ",
"phone": "132********"
},
"office_info": {
"tel": "010-1111111"
}
},
"row_key2":{
......
}
2.2 HBase 物理存儲(chǔ)結(jié)構(gòu)
物理存儲(chǔ)結(jié)構(gòu)即為數(shù)據(jù)映射關(guān)系,而在概念視圖的空單元格,底層實(shí)際根本不存儲(chǔ)
2.3 HBase的表數(shù)據(jù)模型
Name Space
命名空間,類似于關(guān)系型數(shù)據(jù)庫的 database 概念,每個(gè)命名空間下有多個(gè)表。HBase 兩個(gè)自帶的命名空間,分別是 hbase 和 default,hbase 中存放的是 HBase 內(nèi)置的表,default表是用戶默認(rèn)使用的命名空間
Table
類似于關(guān)系型數(shù)據(jù)庫的表概念。不同的是,HBase 定義表時(shí)只需要聲明列族即可,不需要聲明具體的列。因?yàn)閿?shù)據(jù)存儲(chǔ)時(shí)稀疏的,所有往 HBase 寫入數(shù)據(jù)時(shí),字段可以動(dòng)態(tài)、按需指定。因此,和關(guān)系型數(shù)據(jù)庫相比,HBase 能夠輕松應(yīng)對(duì)字段變更的場景
行鍵 Row Key
HBase 表中的每行數(shù)據(jù)都由一個(gè) RowKey 和多個(gè) Column(列)組成,數(shù)據(jù)是按照 RowKey 的字典順序存儲(chǔ)的,并且查詢數(shù)據(jù)時(shí)只能根據(jù) RowKey 進(jìn)行檢索,所以 RowKey 的設(shè)計(jì)十分重要。與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會(huì)對(duì)表中的數(shù)據(jù)按照rowkey排序(字典順序)。存儲(chǔ)時(shí),數(shù)據(jù)按照Row key的字典序(byte order)排序存儲(chǔ)。設(shè)計(jì)key時(shí),要充分排序存儲(chǔ)這個(gè)特性,將經(jīng)常一起讀取的行存儲(chǔ)放到一起。(位置相關(guān)性)。
注意: 字典序?qū)nt排序的結(jié)果是 1,10,100,11,12,13,14,15,16,17,18,19,2,20,21 … 。要保持整形的自然序,行鍵必須用0作左填充。行的一次讀寫是原子操作 (不論一次讀寫多少列)。這個(gè)設(shè)計(jì)決策能夠使用戶很容易的理解程序在對(duì)同一個(gè)行進(jìn)行并發(fā)更新操作時(shí)的行為。
列族 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è)置太多的列族。
列 Column
HBase 中的每個(gè)列都由 **Column Family(列族)**和 **Column Qualifier(列限定符)**進(jìn)行限定,例如 info:name,info:age。建表時(shí),只需指明列族,而列限定符無需預(yù)先定義
時(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)
用戶可以針對(duì)每個(gè)列族進(jìn)行設(shè)置
單元 Cell
由{rowkey, column Family:column Qualifier, timestamp} 唯一確定的單元。cell 中的數(shù)據(jù)全部是字節(jié)碼形式存貯
版本號(hào) VersionNum
數(shù)據(jù)的版本號(hào),每條數(shù)據(jù)可以有多個(gè)版本號(hào),默認(rèn)值為系統(tǒng)時(shí)間戳,類型為Long
3、HBase 基本架構(gòu)
3.1 Master
實(shí)現(xiàn)類為 HMaster,負(fù)責(zé)監(jiān)控集群中所有的 RegionServer 實(shí)例。主要作用如下:
管理元數(shù)據(jù)表格 hbase:meta,接收用戶對(duì)表格創(chuàng)建修改刪除的命令并執(zhí)行監(jiān)控 region 是否需要進(jìn)行負(fù)載均衡,故障轉(zhuǎn)移和 region 的拆分;通過啟動(dòng)多個(gè)后臺(tái)線程監(jiān)控實(shí)現(xiàn)上述功能:
LoadBalancer 負(fù)載均衡器:周期性監(jiān)控 region 分布在 regionServer 上面是否均衡,由參數(shù) hbase.balancer.period 控制周期時(shí)間,默認(rèn) 5 分鐘。CatalogJanitor 元數(shù)據(jù)管理器:定期檢查和清理 hbase:meta 中的數(shù)據(jù)。meta 表內(nèi)容在進(jìn)階中介紹MasterProcWAL master 預(yù)寫日志處理器:把 master 需要執(zhí)行的任務(wù)記錄到預(yù)寫日志 WAL 中,如果 master 宕機(jī),讓 backupMaster讀取日志繼續(xù)干
總結(jié)
監(jiān)控 RegionServer處理 RegionServer 故障轉(zhuǎn)移處理元數(shù)據(jù)的變更處理 region 的分配或移除在空閑時(shí)間進(jìn)行數(shù)據(jù)的負(fù)載均衡通過 Zookeeper 發(fā)布自己的位置給客戶端
3.2 Region Server
Region Server 實(shí)現(xiàn)類為 HRegionServer,主要作用如下:
負(fù)責(zé)存儲(chǔ) HBase 的實(shí)際數(shù)據(jù)處理分配給它的 Region刷新緩存到 HDFS維護(hù) HLog執(zhí)行壓縮負(fù)責(zé)處理 Region 分片
幾個(gè)組件如下
Write-Ahead logs
HBase 的修改記錄,當(dāng)對(duì) HBase 讀寫數(shù)據(jù)的時(shí)候,數(shù)據(jù)不是直接寫進(jìn)磁盤,它會(huì)在內(nèi)存中保留一段時(shí)間(時(shí)間以及數(shù)據(jù)量閾值可以設(shè)定)。但把數(shù)據(jù)保存在內(nèi)存中可能有更高的概率引起數(shù)據(jù)丟失,為了解決這個(gè)問題,數(shù)據(jù)會(huì)先寫在一個(gè)叫做 Write-Ahead logfile 的文件中,然后再寫入內(nèi)存中。所以在系統(tǒng)出現(xiàn)故障的時(shí)候,數(shù)據(jù)可以通過這個(gè)日志文件重建
HFile
這是在磁盤上保存原始數(shù)據(jù)的實(shí)際的物理文件,是實(shí)際的存儲(chǔ)文件
Store
HFile 存儲(chǔ)在 Store 中,一個(gè) Store 對(duì)應(yīng) HBase 表中的一個(gè)列族。
MemStore
顧名思義,就是內(nèi)存存儲(chǔ),位于內(nèi)存中,用來保存當(dāng)前的數(shù)據(jù)操作,所以當(dāng)數(shù)據(jù)保存在 WAL 中之后,RegsionServer 會(huì)在內(nèi)存中存儲(chǔ)鍵值對(duì)
Region
Hbase 表的分片,HBase 表會(huì)根據(jù) RowKey 值被切分成不同的 region 存儲(chǔ)在 RegionServer 中,在一個(gè) RegionServer 中可以有多個(gè)不同的 region
3.3 Zookeeper
HBase 通過 Zookeeper 來做 master 的高可用、記錄 RegionServer 的部署信息、并且存儲(chǔ)有 meta 表的位置信息。
HBase 對(duì)于數(shù)據(jù)的讀寫操作時(shí)直接訪問 Zookeeper 的,在 2.3 版本推出 Master Registry模式,客戶端可以直接訪問 master。使用此功能,會(huì)加大對(duì) master 的壓力,減輕對(duì) Zookeeper的壓力。
3.4 HDFS
HDFS 為 Hbase 提供最終的底層數(shù)據(jù)存儲(chǔ)服務(wù),同時(shí)為 HBase 提供高容錯(cuò)的支持
二、HBase 快速入門
1、HBase 安裝部署
1.1 前置環(huán)境與下載
# HBase啟動(dòng)需要有zookeeper和hadoop,這個(gè)可以參考之前的文章
# 三臺(tái)服務(wù)器開啟zookeeper
bin/zkServer.sh start
# 開啟hadoop
sbin/start-dfs.sh
sbin/start-yarn.sh
# 然后下載hbase
# https://archive.apache.org/dist/hbase/
wget https://archive.apache.org/dist/hbase/2.4.11/hbase-2.4.11-bin.tar.gz
tar -zxvf hbase-2.4.11-bin.tar.gz -C /opt/module/
mv /opt/module/hbase-2.4.11 /opt/module/hbase
# 配置環(huán)境變量
sudo vim /etc/profile.d/my_env.sh
# 添加
#HBASE_HOME
export HBASE_HOME=/opt/module/hbase
export PATH=$PATH:$HBASE_HOME/bin
# 使用 source 讓配置的環(huán)境變量生效
source /etc/profile.d/my_env.sh
1.2 HBase 的配置文件
cd /opt/module/hbase/conf
# hbase-env.sh 修改內(nèi)容
vim hbase-env.sh
# hbase-env.sh 修改內(nèi)容,可以添加到最后,默認(rèn)是依賴自己,即開箱即用
export HBASE_MANAGES_ZK=false
修改hbase-site.xml ,vim hbase-site.xml
然后修改regionservers,類似hadoop的workers
hadoop102
hadoop103
hadoop104
最后解決 HBase 和 Hadoop 的 log4j 兼容性問題,修改 HBase 的 jar 包,使用 Hadoop 的 jar 包
mv /opt/module/hbase/lib/client-facing-thirdparty/slf4j-reload4j-1.7.33.jar /opt/module/hbase/lib/client-facing-thirdparty/slf4j-reload4j-1.7.33.jar.bak
1.3 分發(fā)與啟動(dòng)
# 分發(fā)hbase配置文件
xsync /opt/module/hbase/
# HBase 服務(wù)的啟動(dòng)
bin/hbase-daemon.sh start master
bin/hbase-daemon.sh start regionserver
# 群啟
bin/start-hbase.sh
bin/stop-hbase.sh
# 啟動(dòng)成功后,可以通過“host:port”的方式來訪問 HBase 管理頁面
# http://hadoop102:16010
1.4 高可用(可選,推薦)
在 HBase 中 HMaster 負(fù)責(zé)監(jiān)控 HRegionServer 的生命周期,均衡 RegionServer 的負(fù)載, 如果HMaster 掛掉了,那么整個(gè) HBase 集群將陷入不健康的狀態(tài),并且此時(shí)的工作狀態(tài)并不會(huì)維持太久。所以HBase 支持對(duì) HMaster 的高可用配置
# 關(guān)閉 HBase 集群(如果沒有開啟則跳過此步)
bin/stop-hbase.sh
# 在 conf 目錄下創(chuàng)建 backup-masters 文件
touch conf/backup-masters
# 在 backup-masters 文件中配置高可用 HMaster 節(jié)點(diǎn),注意可以寫多個(gè)
echo hadoop103 > conf/backup-masters
# 將整個(gè) conf 目錄 scp 到其他節(jié)點(diǎn)
xsync conf
# 重啟 hbase,打開頁面測試查看
bin/start-hbase.sh
bin/stop-hbase.sh
# 宕機(jī)機(jī)器開啟,但也只會(huì)變成backup,注意,如果要停止的話需要進(jìn)入master那臺(tái)機(jī)器停止,因?yàn)閎ackup沒有集群信息
bin/hbase-daemon.sh start master
2、HBase Shell 基本操作
2.1 基本操作
# 進(jìn)入 HBase 客戶端命令行
bin/hbase shell
# 查看幫助命令
# 能夠展示 HBase 中所有能使用的命令,主要使用的命令有 namespace 命令空間相關(guān),DDL 創(chuàng)建修改表格,DML 寫入讀取數(shù)據(jù)
hbase:001:0> help
# namespace
# 創(chuàng)建命名空間
hbase:002:0> help 'create_namespace'
# 創(chuàng)建命名空間 bigdata
hbase:003:0> create_namespace 'bigdata'
# 查看所有的命名空間
hbase:004:0> list_namespace
2.2 DDL
# DDL
# 創(chuàng)建表
# 在 bigdata 命名空間中創(chuàng)建表格 student,兩個(gè)列族。info 列族數(shù)據(jù)維護(hù)的版本數(shù)為 5 個(gè),如果不寫默認(rèn)版本數(shù)為 1
hbase:005:0> create 'bigdata:student', {NAME => 'info', VERSIONS => 5}, {NAME => 'msg'}
# 如果創(chuàng)建表格只有一個(gè)列族,沒有列族屬性,可以簡寫
# 如果不寫命名空間,使用默認(rèn)的命名空間 default
hbase:009:0> create 'student1','info'
# 查看表
# 查看表有兩個(gè)命令:list 和 describe
hbase:013:0> list
# describe:查看一個(gè)表的詳情
hbase:014:0> describe 'student1'
# 修改表
# 表名創(chuàng)建時(shí)寫的所有和列族相關(guān)的信息,都可以后續(xù)通過 alter 修改,包括增加刪除列族
# 增加列族和修改信息都使用覆蓋的方法
hbase:015:0> alter 'student1', {NAME => 'f1', VERSIONS => 3}
# 刪除信息使用特殊的語法
hbase:015:0> alter 'student1', NAME => 'f1', METHOD => 'delete'
hbase:016:0> alter 'student1', 'delete' => 'f1'
# 刪除表
# shell 中刪除表格,需要先將表格狀態(tài)設(shè)置為不可用
hbase:017:0> disable 'student1'
hbase:018:0> drop 'student1'
2.3 DML
# DML
# 寫入數(shù)據(jù)
# 在 HBase 中如果想要寫入數(shù)據(jù),只能添加結(jié)構(gòu)中最底層的 cell??梢允謩?dòng)寫入時(shí)間戳指定 cell 的版本,推薦不寫默認(rèn)使用當(dāng)前的系統(tǒng)時(shí)間。
hbase:019:0> put 'bigdata:student','1001','info:name','zhangsan'
hbase:020:0> put 'bigdata:student','1001','info:name','lisi'
hbase:021:0> put 'bigdata:student','1001','info:age','18'
# 如果重復(fù)寫入相同 rowKey,相同列的數(shù)據(jù),會(huì)寫入多個(gè)版本進(jìn)行覆蓋
# 讀取數(shù)據(jù)
# 讀取數(shù)據(jù)的方法有兩個(gè):get 和 scan
# get 最大范圍是一行數(shù)據(jù),也可以進(jìn)行列的過濾,讀取數(shù)據(jù)的結(jié)果為多行 cell
hbase:022:0> get 'bigdata:student','1001'
hbase:023:0> get 'bigdata:student','1001' , {COLUMN => 'info:name'}
# 也可以修改讀取 cell 的版本數(shù),默認(rèn)讀取一個(gè)。最多能夠讀取當(dāng)前列族設(shè)置的維護(hù)版本數(shù)
hbase:024:0>get 'bigdata:student','1001' , {COLUMN => 'info:name', VERSIONS => 6}
# scan 是掃描數(shù)據(jù),能夠讀取多行數(shù)據(jù),不建議掃描過多的數(shù)據(jù),推薦使用 startRow 和stopRow 來控制讀取的數(shù)據(jù),默認(rèn)范圍左閉右開
hbase:025:0> scan 'bigdata:student',{STARTROW => '1001',STOPROW => '1002'}
# 實(shí)際開發(fā)中使用 shell 的機(jī)會(huì)不多,所有豐富的使用方法到 API 中介紹
# 刪除數(shù)據(jù)
# 刪除數(shù)據(jù)的方法有兩個(gè):delete 和 deleteall
# delete 表示刪除一個(gè)版本的數(shù)據(jù),即為 1 個(gè) cell,不填寫版本默認(rèn)刪除最新的一個(gè)版本
hbase:026:0> delete 'bigdata:student','1001','info:name'
# deleteall 表示刪除所有版本的數(shù)據(jù),即為當(dāng)前行當(dāng)前列的多個(gè) cell。(執(zhí)行命令會(huì)標(biāo)記數(shù)據(jù)為要?jiǎng)h除,不會(huì)直接將數(shù)據(jù)徹底刪除,刪除數(shù)據(jù)只在特定時(shí)期清理磁盤時(shí)進(jìn)行)
hbase:027:0> deleteall 'bigdata:student','1001','info:name'
2.4 其他
# 顯示服務(wù)器狀態(tài)
status 'node01'
# whoami顯示 HBase 當(dāng)前用戶
whoami
# 顯示當(dāng)前所有的表
list
# 統(tǒng)計(jì)指定表的記錄數(shù)
count 'user'
# 展示表結(jié)構(gòu)信息
describe 'user'
# 檢查表是否存在,適用于表量特別多的情況
exists 'user'
# 檢查表是否啟用或禁用:is_enabled、is_disabled
is_enabled 'user'
# truncate清空表
三、HBase API
代碼較為底層,一般二次開發(fā)jar包引入使用
1、環(huán)境準(zhǔn)備與連接
1.1 環(huán)境準(zhǔn)備
新建項(xiàng)目后在 pom.xml 中添加依賴,注意:會(huì)報(bào)錯(cuò) javax.el 包不存在,是一個(gè)測試用的依賴,不影響使用
1.2 單線程連接
HBase 的客戶端連接由 ConnectionFactory 類來創(chuàng)建,用戶使用完成之后需要手動(dòng)關(guān)閉連接。同時(shí)連接是一個(gè)重量級(jí)的,推薦一個(gè)進(jìn)程使用一個(gè)連接,對(duì) HBase 的命令通過連接中的兩個(gè)屬性 Admin 和 Table 來實(shí)現(xiàn)
public class HBaseConnect {
public static void main(String[] args) throws IOException {
// 1. 創(chuàng)建配置對(duì)象
Configuration conf = new Configuration();
// 2. 添加配置參數(shù)
conf.set("hbase.zookeeper.quorum", "hadoop102,hadoop103,hadoop104");
// 3. 創(chuàng)建 hbase 的連接
// 默認(rèn)使用同步連接
Connection connection =
ConnectionFactory.createConnection(conf);
// 可以使用異步連接
// 主要影響后續(xù)的 DML 操作
CompletableFuture
ConnectionFactory.createAsyncConnection(conf);
// 4. 使用連接
System.out.println(connection);
// 5. 關(guān)閉連接
connection.close();
}
}
1.3 多線程創(chuàng)建連接(推薦)
使用類單例模式,確保使用一個(gè)連接,可以同時(shí)用于多個(gè)線程
public class HBaseConnect {
// 設(shè)置靜態(tài)屬性 hbase 連接
public static Connection connection = null;
static {
// 創(chuàng)建 hbase 的連接
try {
// 使用配置文件的方法
connection = ConnectionFactory.createConnection();
} catch (IOException e) {
System.out.println("連接獲取失敗");
e.printStackTrace();
}
}
/**
* 連接關(guān)閉方法,用于進(jìn)程關(guān)閉時(shí)調(diào)用
*
* @throws IOException
*/
public static void closeConnection() throws IOException {
if (connection != null) {
connection.close();
}
}
}
在 resources 文件夾中創(chuàng)建配置文件 hbase-site.xml,添加以下內(nèi)容
2、DDL
創(chuàng)建 HBaseDDL 類,添加靜態(tài)方法即可作為工具類
public class HBaseDDL {
// 添加靜態(tài)屬性 connection 指向單例連接
public static Connection connection = HBaseConnect.connection;
}
2.1 創(chuàng)建命名空間
/**
* 創(chuàng)建命名空間
*
* @param namespace 命名空間名稱
*/
public static void createNamespace(String namespace) throws
IOException {
// 1. 獲取 admin
// 此處的異常先不要拋出 等待方法寫完 再統(tǒng)一進(jìn)行處理
// admin 的連接是輕量級(jí)的 不是線程安全的 不推薦池化或者緩存這個(gè)連接
Admin admin = connection.getAdmin();
// 2. 調(diào)用方法創(chuàng)建命名空間
// 代碼相對(duì) shell 更加底層 所以 shell 能夠?qū)崿F(xiàn)的功能 代碼一定能實(shí)現(xiàn)
// 所以需要填寫完整的命名空間描述
// 2.1 創(chuàng)建命令空間描述建造者 => 設(shè)計(jì)師
NamespaceDescriptor.Builder builder =
NamespaceDescriptor.create(namespace);
// 2.2 給命令空間添加需求
builder.addConfiguration("user", "atguigu");
// 2.3 使用 builder 構(gòu)造出對(duì)應(yīng)的添加完參數(shù)的對(duì)象 完成創(chuàng)建
// 創(chuàng)建命名空間出現(xiàn)的問題 都屬于本方法自身的問題 不應(yīng)該拋出
try {
admin.createNamespace(builder.build());
} catch (IOException e) {
System.out.println("命令空間已經(jīng)存在");
e.printStackTrace();
}
// 3. 關(guān)閉 admin
admin.close();
}
2.2 判斷表格是否存在
/**
* 判斷表格是否存在
* @param namespace 命名空間名稱
* @param tableName 表格名稱
* @return ture 表示存在
*/
public static boolean isTableExists(String namespace,String
tableName) throws IOException {
// 1. 獲取 admin
Admin admin = connection.getAdmin();
// 2. 使用方法判斷表格是否存在
boolean b = false;
try {
b = admin.tableExists(TableName.valueOf(namespace, tableName));
} catch (IOException e) {
e.printStackTrace();
}
// 3. 關(guān)閉 admin
admin.close();
// 3. 返回結(jié)果
return b;
// 后面的代碼不能生效
}
2.3 創(chuàng)建表
/**
* 創(chuàng)建表格
* @param namespace 命名空間名稱
* @param tableName 表格名稱
* @param columnFamilies 列族名稱 可以有多個(gè)
*/
public static void createTable(String namespace , String
tableName , String... columnFamilies) throws IOException {
// 判斷是否有至少一個(gè)列族
if (columnFamilies.length == 0){
System.out.println("創(chuàng)建表格至少有一個(gè)列族");
return;
}
// 判斷表格是否存在
if (isTableExists(namespace,tableName)){
System.out.println("表格已經(jīng)存在");
return;
}
// 1.獲取 admin
Admin admin = connection.getAdmin();
// 2. 調(diào)用方法創(chuàng)建表格
// 2.1 創(chuàng)建表格描述的建造者
TableDescriptorBuilder tableDescriptorBuilder =
TableDescriptorBuilder.newBuilder(TableName.valueOf(namespace, tableName));
// 2.2 添加參數(shù)
for (String columnFamily : columnFamilies) {
// 2.3 創(chuàng)建列族描述的建造者
ColumnFamilyDescriptorBuilder
columnFamilyDescriptorBuilder = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(columnFamily));
// 2.4 對(duì)應(yīng)當(dāng)前的列族添加參數(shù)
// 添加版本參數(shù)
columnFamilyDescriptorBuilder.setMaxVersions(5);
// 2.5 創(chuàng)建添加完參數(shù)的列族描述
tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptorBuilder.build());
}
// 2.6 創(chuàng)建對(duì)應(yīng)的表格描述
try {
admin.createTable(tableDescriptorBuilder.build());
} catch (IOException e) {
e.printStackTrace();
}
// 3. 關(guān)閉 admin
admin.close();
}
2.4 修改表
/**
* 修改表格中一個(gè)列族的版本
*
* @param namespace 命名空間名稱
* @param tableName 表格名稱
* @param columnFamily 列族名稱
* @param version 版本
*/
public static void modifyTable(String namespace, String
tableName, String columnFamily, int version) throws IOException {
// 判斷表格是否存在
if (!isTableExists(namespace, tableName)) {
System.out.println("表格不存在無法修改");
return;
}
// 1. 獲取 admin
Admin admin = connection.getAdmin();
try {
// 2. 調(diào)用方法修改表格
// 2.0 獲取之前的表格描述
TableDescriptor descriptor =
admin.getDescriptor(TableName.valueOf(namespace, tableName));
// 2.1 創(chuàng)建一個(gè)表格描述建造者
// 如果使用填寫 tableName 的方法 相當(dāng)于創(chuàng)建了一個(gè)新的表格描述建造者沒有之前的信息
// 如果想要修改之前的信息 必須調(diào)用方法填寫一個(gè)舊的表格描述
TableDescriptorBuilder tableDescriptorBuilder =
TableDescriptorBuilder.newBuilder(descriptor);
// 2.2 對(duì)應(yīng)建造者進(jìn)行表格數(shù)據(jù)的修改
ColumnFamilyDescriptor columnFamily1 =
descriptor.getColumnFamily(Bytes.toBytes(columnFamily));
// 創(chuàng)建列族描述建造者
// 需要填寫舊的列族描述
ColumnFamilyDescriptorBuilder
columnFamilyDescriptorBuilder = ColumnFamilyDescriptorBuilder.newBuilder(columnFamily1);
// 修改對(duì)應(yīng)的版本
columnFamilyDescriptorBuilder.setMaxVersions(version);
// 此處修改的時(shí)候 如果填寫的新創(chuàng)建 那么別的參數(shù)會(huì)初始化
tableDescriptorBuilder.modifyColumnFamily(columnFamilyDescriptorBuilder.build());
admin.modifyTable(tableDescriptorBuilder.build());
} catch (IOException e) {
e.printStackTrace();
}
// 3. 關(guān)閉 admin
admin.close();
}
2.5 刪除表
/**
* 刪除表格
* @param namespace 命名空間名稱
* @param tableName 表格名稱
* @return true 表示刪除成功
*/
public static boolean deleteTable(String namespace ,String tableName) throws IOException {
// 1. 判斷表格是否存在
if (!isTableExists(namespace,tableName)){
System.out.println("表格不存在 無法刪除");
return false;
}
// 2. 獲取 admin
Admin admin = connection.getAdmin();
// 3. 調(diào)用相關(guān)的方法刪除表格
try {
// HBase 刪除表格之前 一定要先標(biāo)記表格為不可以
TableName tableName1 = TableName.valueOf(namespace, tableName);
admin.disableTable(tableName1);
admin.deleteTable(tableName1);
} catch (IOException e) {
e.printStackTrace();
}
// 4. 關(guān)閉 admin
admin.close();
return true;
}
3、DML
創(chuàng)建類 HBaseDML
public class HBaseDML {
// 添加靜態(tài)屬性 connection 指向單例連接
public static Connection connection = HBaseConnect.connection;
}
3.1 插入數(shù)據(jù)
/**
* 插入數(shù)據(jù)
* @param namespace 命名空間名稱
* @param tableName 表格名稱
* @param rowKey 主鍵
* @param columnFamily 列族名稱
* @param columnName 列名
* @param value 值
*/
public static void putCell(String namespace,String
tableName,String rowKey, String columnFamily,String
columnName,String value) throws IOException {
// 1. 獲取 table
Table table = connection.getTable(TableName.valueOf(namespace, tableName));
// 2. 調(diào)用相關(guān)方法插入數(shù)據(jù)
// 2.1 創(chuàng)建 put 對(duì)象
Put put = new Put(Bytes.toBytes(rowKey));
// 2.2. 給 put 對(duì)象添加數(shù)據(jù)
put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnName), Bytes.toBytes(value));
// 2.3 將對(duì)象寫入對(duì)應(yīng)的方法
try {
table.put(put);
} catch (IOException e) {
e.printStackTrace();
}
// 3. 關(guān)閉 table
table.close();
}
3.2 讀取數(shù)據(jù)
/**
* 讀取數(shù)據(jù) 讀取對(duì)應(yīng)的一行中的某一列
*
* @param namespace 命名空間名稱
* @param tableName 表格名稱
* @param rowKey 主鍵
* @param columnFamily 列族名稱
* @param columnName 列名
*/
public static void getCells(String namespace, String tableName,
String rowKey, String columnFamily, String columnName) throws
IOException {
// 1. 獲取 table
Table table =connection.getTable(TableName.valueOf(namespace, tableName));
// 2. 創(chuàng)建 get 對(duì)象
Get get = new Get(Bytes.toBytes(rowKey));
// 如果直接調(diào)用 get 方法讀取數(shù)據(jù) 此時(shí)讀一整行數(shù)據(jù)
// 如果想讀取某一列的數(shù)據(jù) 需要添加對(duì)應(yīng)的參數(shù)
get.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnName));
// 設(shè)置讀取數(shù)據(jù)的版本
get.readAllVersions();
try {
// 讀取數(shù)據(jù) 得到 result 對(duì)象
Result result = table.get(get);
// 處理數(shù)據(jù)
Cell[] cells = result.rawCells();
// 測試方法: 直接把讀取的數(shù)據(jù)打印到控制臺(tái)
// 如果是實(shí)際開發(fā) 需要再額外寫方法 對(duì)應(yīng)處理數(shù)據(jù)
for (Cell cell : cells) {
// cell 存儲(chǔ)數(shù)據(jù)比較底層
String value = new String(CellUtil.cloneValue(cell));
System.out.println(value);
}
} catch (IOException e) {
e.printStackTrace();
}
// 關(guān)閉 table
table.close();
}
3.3 掃描數(shù)據(jù)
/**
* 掃描數(shù)據(jù)
*
* @param namespace 命名空間
* @param tableName 表格名稱
* @param startRow 開始的 row 包含的
* @param stopRow 結(jié)束的 row 不包含
*/
public static void scanRows(String namespace, String tableName,
String startRow, String stopRow) throws IOException {
// 1. 獲取 table
Table table = connection.getTable(TableName.valueOf(namespace, tableName));
// 2. 創(chuàng)建 scan 對(duì)象
Scan scan = new Scan();
// 如果此時(shí)直接調(diào)用 會(huì)直接掃描整張表
// 添加參數(shù) 來控制掃描的數(shù)據(jù)
// 默認(rèn)包含
scan.withStartRow(Bytes.toBytes(startRow));
// 默認(rèn)不包含
scan.withStopRow(Bytes.toBytes(stopRow));
try {
// 讀取多行數(shù)據(jù) 獲得 scanner
ResultScanner scanner = table.getScanner(scan);
// result 來記錄一行數(shù)據(jù) cell 數(shù)組
// ResultScanner 來記錄多行數(shù)據(jù) result 的數(shù)組
for (Result result : scanner) {
Cell[] cells = result.rawCells();
for (Cell cell : cells) {
System.out.print (new String(CellUtil.cloneRow(cell)) + "-" + new
String(CellUtil.cloneFamily(cell)) + "-" + new
String(CellUtil.cloneQualifier(cell)) + "-" + new
String(CellUtil.cloneValue(cell)) + "\t");
}
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
}
// 3. 關(guān)閉 table
table.close();
}
3.4 帶過濾掃描
/**
* 帶過濾的掃描
*
* @param namespace 命名空間
* @param tableName 表格名稱
* @param startRow 開始 row
* @param stopRow 結(jié)束 row
* @param columnFamily 列族名稱
* @param columnName 列名
* @param value value 值
* @throws IOException
*/
public static void filterScan(String namespace, String tableName,
String startRow, String stopRow, String columnFamily, String
columnName, String value) throws IOException {
// 1. 獲取 table
Table table = connection.getTable(TableName.valueOf(namespace, tableName));
// 2. 創(chuàng)建 scan 對(duì)象
Scan scan = new Scan();
// 如果此時(shí)直接調(diào)用 會(huì)直接掃描整張表
// 添加參數(shù) 來控制掃描的數(shù)據(jù)
// 默認(rèn)包含
scan.withStartRow(Bytes.toBytes(startRow));
// 默認(rèn)不包含
scan.withStopRow(Bytes.toBytes(stopRow));
// 可以添加多個(gè)過濾
FilterList filterList = new FilterList();
// 創(chuàng)建過濾器
// (1) 結(jié)果只保留當(dāng)前列的數(shù)據(jù)
ColumnValueFilter columnValueFilter = new ColumnValueFilter(
// 列族名稱
Bytes.toBytes(columnFamily),
// 列名
Bytes.toBytes(columnName),
// 比較關(guān)系
CompareOperator.EQUAL,
// 值
Bytes.toBytes(value)
);
// (2) 結(jié)果保留整行數(shù)據(jù)
// 結(jié)果同時(shí)會(huì)保留沒有當(dāng)前列的數(shù)據(jù)
SingleColumnValueFilter singleColumnValueFilter = new
SingleColumnValueFilter(
// 列族名稱
Bytes.toBytes(columnFamily),
// 列名
Bytes.toBytes(columnName),
// 比較關(guān)系
CompareOperator.EQUAL,
// 值
Bytes.toBytes(value)
);
// 本身可以添加多個(gè)過濾器
filterList.addFilter(singleColumnValueFilter);
// 添加過濾
scan.setFilter(filterList);
try {
// 讀取多行數(shù)據(jù) 獲得 scanner
ResultScanner scanner = table.getScanner(scan);
// result 來記錄一行數(shù)據(jù) cell 數(shù)組
// ResultScanner 來記錄多行數(shù)據(jù) result 的數(shù)組
for (Result result : scanner) {
Cell[] cells = result.rawCells();
for (Cell cell : cells) {
System.out.print(new
String(CellUtil.cloneRow(cell)) + "-" + new
String(CellUtil.cloneFamily(cell)) + "-" + new
String(CellUtil.cloneQualifier(cell)) + "-" + new
String(CellUtil.cloneValue(cell)) + "\t");
}
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
}
// 3. 關(guān)閉 table
table.close();
}
3.5 刪除數(shù)據(jù)
/**
* 刪除 column 數(shù)據(jù)
*
* @param nameSpace
* @param tableName
* @param rowKey
* @param family
* @param column
* @throws IOException
*/
public static void deleteColumn(String nameSpace, String tableName,
String rowKey, String family, String column) throws IOException {
// 1.獲取 table
Table table = connection.getTable(TableName.valueOf(nameSpace, tableName));
// 2.創(chuàng)建 Delete 對(duì)象
Delete delete = new Delete(Bytes.toBytes(rowKey));
// 3.添加刪除信息
// 3.1 刪除單個(gè)版本
delete.addColumn(Bytes.toBytes(family),Bytes.toBytes(column));
// 3.2 刪除所有版本
delete.addColumns(Bytes.toBytes(family), Bytes.toBytes(column));
// 3.3 刪除列族
// delete.addFamily(Bytes.toBytes(family));
// 3.刪除數(shù)據(jù)
table.delete(delete);
// 5.關(guān)閉資源
table.close();
}
四、HBase 原理分析
官方文檔:https://hbase.apache.org/2.4/book.html
1、架構(gòu)分析
1.1 Master 架構(gòu)
Meta 表格介紹:(警告:不要去改這個(gè)表),全稱 hbase:meta,只是在 list 命令中被過濾掉了,本質(zhì)上和 HBase 的其他表格一樣。
RowKey:
([table],[region start key],[region id]) 即 表名,region 起始位置和 regionID
列:
info:regioninfo 為 region 信息,存儲(chǔ)一個(gè) HRegionInfo 對(duì)象info:server 當(dāng)前 region 所處的 RegionServer 信息,包含端口號(hào)info:serverstartcode 當(dāng)前 region 被分到 RegionServer 的起始時(shí)間
如果一個(gè)表處于切分的過程中,即 region 切分,還會(huì)多出兩列 info:splitA 和 info:splitB,存儲(chǔ)值也是 HRegionInfo 對(duì)象,拆分結(jié)束后,刪除這兩列。
注意:在客戶端對(duì)元數(shù)據(jù)進(jìn)行操作的時(shí)候才會(huì)連接 master,如果對(duì)數(shù)據(jù)進(jìn)行讀寫,直接連接zookeeper 讀取目錄/hbase/meta-region-server 節(jié)點(diǎn)信息,會(huì)記錄 meta 表格的位置。直接讀取即可,不需要訪問 master,這樣可以減輕 master 的壓力,相當(dāng)于 master 專注 meta 表的寫操作,客戶端可直接讀取 meta 表。在 HBase 的 2.3 版本更新了一種新模式:Master Registry??蛻舳丝梢栽L問 master 來讀取meta 表信息。加大了 master 的壓力,減輕了 zookeeper 的壓力。HMaster僅僅維護(hù)者table和HRegion的元數(shù)據(jù)信息,負(fù)載很低
總結(jié):
為Region server分配region負(fù)責(zé)region server的負(fù)載均衡發(fā)現(xiàn)失效的region server并重新分配其上的regionHDFS上的垃圾文件回收處理schema更新請(qǐng)求
1.2 RegionServer 架構(gòu)
HRegion server維護(hù)HMaster分配給它的region,處理對(duì)這些region的IO請(qǐng)求HRegion server負(fù)責(zé)切分在運(yùn)行過程中變得過大的region 從圖中可以看到,Client訪問HBase上數(shù)據(jù)的過程并不需要HMaster參與(尋址訪問Zookeeper和HRegion server,數(shù)據(jù)讀寫訪問HRegione server)
一些組件介紹
MemStore
寫緩存,由于 HFile 中的數(shù)據(jù)要求是有序的,所以數(shù)據(jù)是先存儲(chǔ)在 MemStore 中,排好序后,等到達(dá)刷寫時(shí)機(jī)才會(huì)刷寫到 HFile,每次刷寫都會(huì)形成一個(gè)新的 HFile,寫入到對(duì)應(yīng)的文件夾 store 中。
一個(gè) HRegion 由多個(gè) Store 組成,每個(gè) Store 包含一個(gè)列族的所有數(shù)據(jù) Store 包括位于內(nèi)存的 Memstore 和位于硬盤的 StoreFile
WAL
由于數(shù)據(jù)要經(jīng) MemStore 排序后才能刷寫到 HFile,但把數(shù)據(jù)保存在內(nèi)存中會(huì)有很高的概率導(dǎo)致數(shù)據(jù)丟失,為了解決這個(gè)問題,數(shù)據(jù)會(huì)先寫在一個(gè)叫做 Write-Ahead logfile 的文件中,然后再寫入 MemStore 中。所以在系統(tǒng)出現(xiàn)故障的時(shí)候,數(shù)據(jù)可以通過這個(gè)日志文件重建
每個(gè)Region Server維護(hù)一個(gè)Hlog,而不是每個(gè)Region一個(gè)。這樣不同region(來自不同table)的日志會(huì)混在一起,這樣做的目的是不斷追加單個(gè)文件相對(duì)于同時(shí)寫多個(gè)文件而言,可以減少磁盤尋址次數(shù),因此可以提高對(duì)table的寫性能。帶來的麻煩是,如果一臺(tái)region server下線,為了恢復(fù)其上的region,需要將region server上的log進(jìn)行拆分,然后分發(fā)到其它region server上進(jìn)行恢復(fù)
BlockCache
讀緩存,每次查詢出的數(shù)據(jù)會(huì)緩存在 BlockCache 中,方便下次查詢
2、HRegion管理和HMaster工作機(jī)制
2.1 HRegion管理
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è)裝載請(qǐng)求,把HRegion分配給這個(gè)HRegion Server。HRegion Server得到請(qǐng)求后,就開始對(duì)此HRegion提供服務(wù)。
HRegion Server上線
HMaster使用zookeeper來跟蹤HRegion Server狀態(tài)。當(dāng)某個(gè)HRegion Server啟動(dòng)時(shí),會(huì)首先在zookeeper上的server目錄下建立代表自己的znode。由于HMaster訂閱了server目錄上的變更消息,當(dāng)server目錄下的文件出現(xiàn)新增或刪除操作時(shí),HMaster可以得到來自zookeeper的實(shí)時(shí)通知。因此一旦HRegion Server上線,HMaster能馬上得到消息。
HRegion Server下線
當(dāng)HRegion Server下線時(shí),它和zookeeper的會(huì)話斷開,zookeeper而自動(dòng)釋放代表這臺(tái)server的文件上的獨(dú)占鎖。HMaster就可以確定:
HRegion Server和zookeeper之間的網(wǎng)絡(luò)斷開了。HRegion Server掛了。
無論哪種情況,HRegion Server都無法繼續(xù)為它的HRegion提供服務(wù)了,此時(shí)HMaster會(huì)刪除server目錄下代表這臺(tái)HRegion Server的znode數(shù)據(jù),并將這臺(tái)HRegion Server的HRegion分配給其它還活著的節(jié)點(diǎn)
2.2 HMaster工作機(jī)制
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的對(duì)應(yīng)關(guān)系。掃描.META.region的集合,計(jì)算得到當(dāng)前還未分配的HRegion,將他們放入待分配HRegion列表。
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)對(duì)整個(gè)HBase集群沒有影響。
從上線過程可以看到,HMaster保存的信息全是可以冗余信息(都可以從系統(tǒng)其它地方收集到或者計(jì)算出來)因此,一般HBase集群中總是有一個(gè)HMaster在提供服務(wù),還有一個(gè)以上的‘HMaster’在等待時(shí)機(jī)搶占它的位置
3、寫流程
寫流程順序正如 API 編寫順序,首先創(chuàng)建 HBase 的重量級(jí)連接
首先訪問 zookeeper,獲取 hbase:meta 表位于哪個(gè) Region Server;訪問對(duì)應(yīng)的 Region Server,獲取 hbase:meta 表,將其緩存到連接中,作為連接屬性 MetaCache,由于 Meta 表格具有一定的數(shù)據(jù)量,導(dǎo)致了創(chuàng)建連接比較慢;之后使用創(chuàng)建的連接獲取 Table,這是一個(gè)輕量級(jí)的連接,只有在第一次創(chuàng)建的時(shí)候會(huì)檢查表格是否存在訪問 RegionServer,之后在獲取 Table 時(shí)不會(huì)訪問 RegionServer;調(diào)用Table的put方法寫入數(shù)據(jù),此時(shí)還需要解析RowKey,對(duì)照緩存的MetaCache,查看具體寫入的位置有哪個(gè) RegionServer;將數(shù)據(jù)順序?qū)懭耄ㄗ芳樱┑?WAL,此處寫入是直接落盤的,并設(shè)置專門的線程控制 WAL 預(yù)寫日志的滾動(dòng)(類似 Flume);根據(jù)寫入命令的 RowKey 和 ColumnFamily 查看具體寫入到哪個(gè) MemStory,并且在 MemStory 中排序;向客戶端發(fā)送 ack;等達(dá)到 MemStore 的刷寫時(shí)機(jī)后,將數(shù)據(jù)刷寫到對(duì)應(yīng)的 story 中
HBase使用MemStore和StoreFile存儲(chǔ)對(duì)表的更新。 數(shù)據(jù)在更新時(shí)首先寫入Log(WAL log)和內(nèi)存(MemStore)中,MemStore中的數(shù)據(jù)是排序的,當(dāng)MemStore累計(jì)到一定閾值時(shí),就會(huì)創(chuàng)建一個(gè)新的MemStore,并且將老的MemStore添加到flush隊(duì)列,由單獨(dú)的線程flush到磁盤上,成為一個(gè)StoreFile。于此同時(shí),系統(tǒng)會(huì)在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á)到一定的閾值后,就會(huì)進(jìn)行一次合并(minor_compact, major_compact),將對(duì)同一個(gè)key的修改合并到一起,形成一個(gè)大的StoreFile,當(dāng)StoreFile的大小達(dá)到一定閾值后,又會(huì)對(duì) StoreFile進(jìn)行split,等分為兩個(gè)StoreFile。
由于對(duì)表的更新是不斷追加的,compact時(shí),需要訪問Store中全部的 StoreFile和MemStore,將他們按row key進(jìn)行合并,由于StoreFile和MemStore都是經(jīng)過排序的,并且StoreFile帶有內(nèi)存中索引,合并的過程還是比較快
4、MemStore Flush
MemStore 刷寫由多個(gè)線程控制,條件互相獨(dú)立,主要的刷寫規(guī)則是控制刷寫文件的大小,在每一個(gè)刷寫線程中都會(huì)進(jìn)行監(jiān)控
刷寫規(guī)則一
當(dāng)某個(gè) memstroe 的大小達(dá)到了 hbase.hregion.memstore.flush.size(默認(rèn)值 128M),其所在 region 的所有 memstore 都會(huì)刷寫。當(dāng) memstore 的大小達(dá)到了
hbase.hregion.memstore.flush.size(默認(rèn)值 128M)hbase.hregion.memstore.block.multiplier(默認(rèn)值 4)
時(shí),會(huì)刷寫同時(shí)阻止繼續(xù)往該 memstore 寫數(shù)據(jù)(由于線程監(jiān)控是周期性的,所有有可能面對(duì)數(shù)據(jù)洪峰,盡管可能性比較?。?/p>
刷寫規(guī)則二
由 HRegionServer 中的屬性 MemStoreFlusher 內(nèi)部線程 FlushHandler 控制。標(biāo)準(zhǔn)為**LOWER_MARK(低水位線)**和 HIGH_MARK(高水位線),意義在于避免寫緩存使用過多的內(nèi)存造成 OOM
當(dāng) region server 中 memstore 的總大小達(dá)到低水位線:
java_heapsizehbase.regionserver.global.memstore.size(默認(rèn)值 0.4)hbase.regionserver.global.memstore.size.lower.limit(默認(rèn)值 0.95,強(qiáng)制刷新之前區(qū)域服務(wù)器中所有內(nèi)存的最大大小。默認(rèn)為hbase.regionserver.global. memory .size(0.95)的95%。如果此值為100%,則在由于內(nèi)存存儲(chǔ)限制而阻塞更新時(shí),會(huì)發(fā)生最小可能的刷新)
region 會(huì)按照其所有 memstore 的大小順序(由大到?。┮来芜M(jìn)行刷寫。直到 region server中所有 memstore 的總大小減小到上述值以下
當(dāng) region server 中 memstore 的總大小達(dá)到高水位線:
java_heapsizehbase.regionserver.global.memstore.size(默認(rèn)值 0.4)
時(shí),會(huì)同時(shí)阻止繼續(xù)往所有的 memstore 寫數(shù)據(jù)
刷寫規(guī)則三
為了避免數(shù)據(jù)過長時(shí)間處于內(nèi)存之中,到達(dá)自動(dòng)刷寫的時(shí)間,也會(huì)觸發(fā) memstoreflush。由 HRegionServer 的屬性 PeriodicMemStoreFlusher 控制進(jìn)行,由于重要性比較低,5min才會(huì)執(zhí)行一次。自動(dòng)刷新的時(shí)間間隔由該屬性進(jìn)行配置hbase.regionserver.optionalcacheflushinterval(默認(rèn)1 小時(shí))
刷寫規(guī)則四
當(dāng) WAL 文件的數(shù)量超過 hbase.regionserver.max.logs,region 會(huì)按照時(shí)間順序依次進(jìn)行刷寫,直到 WAL 文件數(shù)量減小到 hbase.regionserver.max.log 以下(該屬性名已經(jīng)廢棄,現(xiàn)無需手動(dòng)設(shè)置,最大值為 32)
5、讀流程
5.1 HFile 結(jié)構(gòu)
HFile 是存儲(chǔ)在 HDFS 上面每一個(gè) store 文件夾下實(shí)際存儲(chǔ)數(shù)據(jù)的文件。里面存儲(chǔ)多種內(nèi)容。包括數(shù)據(jù)本身(keyValue 鍵值對(duì))、元數(shù)據(jù)記錄、文件信息、數(shù)據(jù)索引、元數(shù)據(jù)索引和一個(gè)固定長度的尾部信息(記錄文件的修改情況)。
鍵值對(duì)按照塊大?。J(rèn) 64K)保存在文件中,數(shù)據(jù)索引按照塊創(chuàng)建,塊越多,索引越大。每一個(gè) HFile 還會(huì)維護(hù)一個(gè)布隆過濾器(就像是一個(gè)很大的地圖,文件中每有一種 key,就在對(duì)應(yīng)的位置標(biāo)記,讀取時(shí)可以大致判斷要 get 的 key 是否存在 HFile 中)。KeyValue 內(nèi)容如下:
rowlength -----------→ key 的長度
row -----------------→ key 的值
columnfamilylength --→ 列族長度
columnfamily --------→ 列族
columnqualifier -----→ 列名
timestamp -----------→ 時(shí)間戳(默認(rèn)系統(tǒng)時(shí)間)
keytype -------------→ Put
由于 HFile 存儲(chǔ)經(jīng)過序列化,所以無法直接查看。可以通過 HBase 提供的命令來查看存儲(chǔ)在 HDFS 上面的 HFile 元數(shù)據(jù)內(nèi)容
bin/hbase hfile -m -f /hbase/data/命名空間/表名/regionID/列族/HFile名
5.2 讀流程
創(chuàng)建連接同寫流程。
創(chuàng)建 Table 對(duì)象發(fā)送 get 請(qǐng)求優(yōu)先訪問 Block Cache,查找是否之前讀取過,并且可以讀取 HFile 的索引信息和布隆過濾器不管讀緩存中是否已經(jīng)有數(shù)據(jù)了(可能已經(jīng)過期了),都需要再次讀取寫緩存和store 中的文件最終將所有讀取到的數(shù)據(jù)合并版本,按照 get 的要求返回即可
5.3 合并讀取數(shù)據(jù)優(yōu)化
每次讀取數(shù)據(jù)都需要讀取三個(gè)位置,最后進(jìn)行版本的合并。效率會(huì)非常低,所有系統(tǒng)需要對(duì)此優(yōu)化
HFile 帶有索引文件,讀取對(duì)應(yīng) RowKey 數(shù)據(jù)會(huì)比較快Block Cache 會(huì)緩存之前讀取的內(nèi)容和元數(shù)據(jù)信息,如果 HFile 沒有發(fā)生變化(記錄在 HFile 尾信息中),則不需要再次讀取使用布隆過濾器能夠快速過濾當(dāng)前 HFile 不存在需要讀取的 RowKey,從而避免讀取文件。(布隆過濾器使用 HASH 算法,不是絕對(duì)準(zhǔn)確的,出錯(cuò)會(huì)造成多掃描一個(gè)文件,對(duì)讀取數(shù)據(jù)結(jié)果沒有影響)
6、StoreFile Compaction
由于 memstore 每次刷寫都會(huì)生成一個(gè)新的 HFile,文件過多讀取不方便,所以會(huì)進(jìn)行文件的合并,清理掉過期和刪除的數(shù)據(jù),會(huì)進(jìn)行 StoreFile Compaction。
Compaction 分為兩種,分別是 Minor Compaction 和** Major Compaction**。Minor Compaction會(huì)將臨近的若干個(gè)較小的 HFile 合并成一個(gè)較大的 HFile,并清理掉部分過期和刪除的數(shù)據(jù),有系統(tǒng)使用一組參數(shù)自動(dòng)控制,Major Compaction 會(huì)將一個(gè) Store 下的所有的 HFile 合并成一個(gè)大 HFile,并且會(huì)清理掉所有過期和刪除的數(shù)據(jù),由參數(shù) hbase.hregion.majorcompaction控制,默認(rèn) 7 天。
Minor Compaction 控制機(jī)制,參與到小合并的文件需要通過參數(shù)計(jì)算得到,有效的參數(shù)有 5 個(gè)
hbase.hstore.compaction.ratio(默認(rèn) 1.2F)合并文件選擇算法中使用的比率hbase.hstore.compaction.min(默認(rèn) 3) 為 Minor Compaction 的最少文件個(gè)數(shù)hbase.hstore.compaction.max(默認(rèn) 10) 為 Minor Compaction 最大文件個(gè)數(shù)hbase.hstore.compaction.min.size(默認(rèn) 128M)為單個(gè) Hfile 文件大小最小值,小于這個(gè)數(shù)會(huì)被合并hbase.hstore.compaction.max.size(默認(rèn) Long.MAX_VALUE)為單個(gè) Hfile 文件大小最大值,高于這個(gè)數(shù)不會(huì)被合并
小合并機(jī)制為拉取整個(gè) store 中的所有文件,做成一個(gè)集合。之后按照從舊到新的順序遍歷。判斷條件為:
過小合并,過大不合并文件大小 / hbase.hstore.compaction.ratio < (剩余文件大小和) 則參與壓縮。所有把比值設(shè)置過大,如 10 會(huì)最終合并為 1 個(gè)特別大的文件,相反設(shè)置為 0.4,會(huì)最終產(chǎn)生 4 個(gè) storeFile。不建議修改默認(rèn)值滿足壓縮條件的文件個(gè)數(shù)達(dá)不到個(gè)數(shù)要求(3 <= count <= 10)則不壓縮
7、Region Split
Region 切分分為兩種,創(chuàng)建表格時(shí)候的預(yù)分區(qū)即自定義分區(qū),同時(shí)系統(tǒng)默認(rèn)還會(huì)啟動(dòng)一個(gè)切分規(guī)則,避免單個(gè) Region 中的數(shù)據(jù)量太大
7.1 預(yù)分區(qū)(自定義分區(qū))
每一個(gè) region 維護(hù)著 startRow 與 endRowKey,如果加入的數(shù)據(jù)符合某個(gè) region 維護(hù)的rowKey 范圍,則該數(shù)據(jù)交給這個(gè) region 維護(hù)。那么依照這個(gè)原則,我們可以將數(shù)據(jù)所要投放的分區(qū)提前大致的規(guī)劃好,以提高 HBase 性能
# 手動(dòng)設(shè)定預(yù)分區(qū)
create 'staff1','info', SPLITS => ['1000','2000','3000','4000']
# 生成 16 進(jìn)制序列預(yù)分區(qū)
create 'staff2','info',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
# 按照文件中設(shè)置的規(guī)則預(yù)分區(qū)
# 創(chuàng)建 splits.txt 文件內(nèi)容如下
aaaa
bbbb
cccc
dddd
# 然后執(zhí)行
create 'staff3', 'info',SPLITS_FILE => 'splits.txt'
最后一種方法是使用java API,不常用也不推薦
public class HBaseConnect {
public static void main(String[] args) throws IOException {
// 1.獲取配置類
Configuration conf = HBaseConfiguration.create();
// 2.給配置類添加配置
conf.set("hbase.zookeeper.quorum","hadoop102,hadoop103,hadoop104"
);
// 3.獲取連接
Connection connection =
ConnectionFactory.createConnection(conf);
// 3.獲取 admin
Admin admin = connection.getAdmin();
// 5.獲取 descriptor 的 builder
TableDescriptorBuilder builder =
TableDescriptorBuilder.newBuilder(TableName.valueOf("bigdata",
"staff4"));
// 6. 添加列族
builder.setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(
Bytes.toBytes("info")).build());
// 7.創(chuàng)建對(duì)應(yīng)的切分
byte[][] splits = new byte[3][];
splits[0] = Bytes.toBytes("aaa");
splits[1] = Bytes.toBytes("bbb");
splits[2] = Bytes.toBytes("ccc");
// 8.創(chuàng)建表
admin.createTable(builder.build(),splits);
// 9.關(guān)閉資源
admin.close();
connection.close();
}
}
7.2 系統(tǒng)拆分
Region 的拆分是由 HRegionServer 完成的,在操作之前需要通過 ZK 匯報(bào) master,修改對(duì)應(yīng)的 Meta 表信息添加兩列 info:splitA 和 info:splitB 信息。之后需要操作 HDFS 上面對(duì)應(yīng)的文件,按照拆分后的 Region 范圍進(jìn)行標(biāo)記區(qū)分,實(shí)際操作為創(chuàng)建文件引用,不會(huì)挪動(dòng)數(shù)據(jù)。剛完成拆分的時(shí)候,兩個(gè) Region 都由原先的RegionServer 管理。之后匯報(bào)給 Master,由Master將修改后的信息寫入到Meta表中。等待下一次觸發(fā)負(fù)載均衡機(jī)制,才會(huì)修改Region的管理服務(wù)者,而數(shù)據(jù)要等到下一次壓縮時(shí),才會(huì)實(shí)際進(jìn)行移動(dòng)。
不管是否使用預(yù)分區(qū),系統(tǒng)都會(huì)默認(rèn)啟動(dòng)一套 Region 拆分規(guī)則。不同版本的拆分規(guī)則有差別。系統(tǒng)拆分策略的父類為 RegionSplitPolicy。
0.94 版本之前 => ConstantSizeRegionSplitPolicy 當(dāng) 1 個(gè) region 中 的 某 個(gè) Store 下 所 有 StoreFile 的 總 大 小 超 過 hbase.hregion.max.filesize (10G),該 Region 就會(huì)進(jìn)行拆分 0.94 版本之后,2.0 版本之前 => IncreasingToUpperBoundRegionSplitPolicy 當(dāng) 1 個(gè) region 中 的 某 個(gè) Store 下 所 有 StoreFile 的 總 大 小 超 過Min(initialSize*R^3 ,hbase.hregion.max.filesize"),該 Region 就會(huì)進(jìn)行拆分。其中 initialSize 的默認(rèn)值為 2*hbase.hregion.memstore.flush.size,R 為當(dāng)前 Region Server 中屬于該 Table 的Region 個(gè)數(shù)(0.94 版本之后),具體的切分策略為:
第一次 split:1^3 * 256 = 256MB第二次 split:2^3 * 256 = 2048MB第三次 split:3^3 * 256 = 6912MB第四次 split:4^3 * 256 = 16384MB > 10GB,因此取較小的值 10GB后面每次 split 的 size 都是 10GB 了。 2.0 版本之后 => SteppingSplitPolicy Hbase 2.0 引入了新的 split 策略:如果當(dāng)前 RegionServer 上該表只有一個(gè) Region,按照 2 * hbase.hregion.memstore.flush.size 分裂,否則按照 hbase.hregion.max.filesize 分裂
五、HBase 優(yōu)化
1、RowKey 設(shè)計(jì)
一條數(shù)據(jù)的唯一標(biāo)識(shí)就是 rowkey,那么這條數(shù)據(jù)存儲(chǔ)于哪個(gè)分區(qū),取決于 rowkey 處于哪個(gè)一個(gè)預(yù)分區(qū)的區(qū)間內(nèi),設(shè)計(jì) rowkey的主要目的 ,就是讓數(shù)據(jù)均勻的分布于所有的 region中,在一定程度上防止數(shù)據(jù)傾斜。接下來我們就談一談 rowkey 常用的設(shè)計(jì)方案
生成隨機(jī)數(shù)、hash、散列值時(shí)間戳反轉(zhuǎn)字符串拼接
現(xiàn)在有一個(gè)需求,源數(shù)據(jù)如下
user data pay
zhangsan 2022-01-05 09:08:00 100
統(tǒng)計(jì)張三在 2021 年 12 月份消費(fèi)的總金額統(tǒng)計(jì)所有人在 2021 年 12 月份消費(fèi)的總金額
1.1 實(shí)現(xiàn)需求 1
為了能夠統(tǒng)計(jì)張三在 2021 年 12 月份消費(fèi)的總金額,我們需要用 scan 命令能夠得到張三在這個(gè)月消費(fèi)的所有記錄,之后在進(jìn)行累加即可。Scan 需要填寫 startRow 和 stopRow:
scan : startRow -> ^A^Azhangsan2021-12
endRow -> ^A^Azhangsan2021-12.
避免掃描數(shù)據(jù)混亂,解決字段長度不一致的問題,可以使用相同阿斯卡碼值的符號(hào)進(jìn)行填充,框架底層填充使用的是阿斯卡碼值為 1 的^A最后的日期結(jié)尾處需要使用阿斯卡碼略大于’-'的值
最終得到 rowKey 的設(shè)計(jì)為
# 注意 rowkey 相同的數(shù)據(jù)會(huì)視為相同數(shù)據(jù)覆蓋掉之前的版本
rowKey: userdate(yyyy-MM-dd HH:mm:SS)
1.2 實(shí)現(xiàn)需求 2
問題提出:按照需要 1 的 rowKey 設(shè)計(jì),會(huì)發(fā)現(xiàn)對(duì)于需求 2,完全沒有辦法寫 rowKey 的掃描范圍。此處能夠看出 hbase 設(shè)計(jì) rowKey 使用的特點(diǎn)為:**適用性強(qiáng) 泛用性差 能夠完美實(shí)現(xiàn)一個(gè)需求 但是不能同時(shí)完美實(shí)現(xiàn)多個(gè)需要。**如果想要同時(shí)完成兩個(gè)需求,需要對(duì) rowKey 出現(xiàn)字段的順序進(jìn)行調(diào)整。調(diào)整的原則為:可枚舉的放在前面。其中時(shí)間是可以枚舉的,用戶名稱無法枚舉,所以必須把時(shí)間放在前面。
# 最終滿足 2 個(gè)需求的設(shè)計(jì),可以窮舉的寫在前面即可
rowKey 設(shè)計(jì)格式 => date(yyyy-MM)^A^Auserdate(-dd hh:mm:ss ms)
#(1)統(tǒng)計(jì)張三在 2021 年 12 月份消費(fèi)的總金額
scan: startRow => 2021-12^A^Azhangsan
stopRow => 2021-12^A^Azhangsan.
#(2)統(tǒng)計(jì)所有人在 2021 年 12 月份消費(fèi)的總金額
scan: startRow => 2021-12
stopRow => 2021-12.
1.3 添加預(yù)分區(qū)優(yōu)化
預(yù)分區(qū)的分區(qū)號(hào)同樣需要遵守 rowKey 的 scan 原則。所有必須添加在 rowKey 的最前面,前綴為最簡單的數(shù)字。同時(shí)使用 hash 算法將用戶名和月份拼接決定分區(qū)號(hào)。(單獨(dú)使用用戶名會(huì)造成單一用戶所有數(shù)據(jù)存儲(chǔ)在一個(gè)分區(qū))
# 添加預(yù)分區(qū)優(yōu)化
startKey stopKey
001
001 002
002 003
...
119 120
# 分區(qū)號(hào)=> hash(user+date(MM)) % 120
# 分區(qū)號(hào)填充 如果得到 1 => 001
rowKey 設(shè)計(jì)格式 => 分區(qū)號(hào) date(yyyy-MM)^A^Auserdate(-dd hh:mm:ss ms)
# 缺點(diǎn):實(shí)現(xiàn)需求 2 的時(shí)候,由于每個(gè)分區(qū)都有 12 月份的數(shù)據(jù),需要掃描 120 個(gè)分區(qū)
# 解決方法:提前將分區(qū)號(hào)和月份進(jìn)行對(duì)應(yīng)
# 提前將月份和分區(qū)號(hào)對(duì)應(yīng)一下
#000 到 009 分區(qū) 存儲(chǔ)的都是 1 月份數(shù)據(jù)
#010 到 019 分區(qū) 存儲(chǔ)的都是 2 月份數(shù)據(jù)
#...
#110 到 119 分區(qū) 存儲(chǔ)的都是 12 月份數(shù)據(jù)
#是 9 月份的數(shù)據(jù)
分區(qū)號(hào)=> hash(user+date(MM)) % 10 + 80
分區(qū)號(hào)填充 如果得到 85 => 085
#得到 12 月份所有人的數(shù)據(jù),掃描 10 次
scan: startRow => 1102021-12
stopRow => 1102021-12.
...
startRow => 1122021-12
stopRow => 1122021-12.
..
startRow => 1192021-12
stopRow => 1192021-12.
2、參數(shù)優(yōu)化
2.1 Zookeeper 會(huì)話超時(shí)時(shí)間
進(jìn)入hbase-site.xml修改
屬性:zookeeper.session.timeout解釋:默認(rèn)值為 90000 毫秒(90s)。當(dāng)某個(gè) RegionServer 掛掉,90s 之后 Master 才能察覺到??蛇m當(dāng)減小此值,盡可能快地檢測 regionserver 故障,可調(diào)整至 20-30s
# 看你能有都能忍耐超時(shí),同時(shí)可以調(diào)整重試時(shí)間和重試次數(shù)
hbase.client.pause(默認(rèn)值 100ms)
hbase.client.retries.number(默認(rèn) 15 次)
2.2 設(shè)置 RPC 監(jiān)聽數(shù)量
屬性:hbase.regionserver.handler.count解釋:默認(rèn)值為 30,用于指定 RPC 監(jiān)聽的數(shù)量,可以根據(jù)客戶端的請(qǐng)求數(shù)進(jìn)行調(diào)整,讀寫請(qǐng)求較多時(shí),增加此值。
2.3 手動(dòng)控制 Major Compaction
屬性:hbase.hregion.majorcompaction解釋:默認(rèn)值:604800000 秒(7 天), Major Compaction 的周期,若關(guān)閉自動(dòng) Major Compaction,可將其設(shè)為 0。如果關(guān)閉一定記得自己手動(dòng)合并,因?yàn)榇蠛喜⒎浅S幸饬x
2.4 優(yōu)化 HStore 文件大小
屬性:hbase.hregion.max.filesize解釋:默認(rèn)值 10737418240(10GB),如果需要運(yùn)行 HBase 的 MR 任務(wù),可以減小此值,因?yàn)橐粋€(gè) region 對(duì)應(yīng)一個(gè) map 任務(wù),如果單個(gè) region 過大,會(huì)導(dǎo)致 map 任務(wù)執(zhí)行時(shí)間過長。該值的意思就是,如果 HFile 的大小達(dá)到這個(gè)數(shù)值,則這個(gè) region 會(huì)被切分為兩個(gè) Hfile
2.5 優(yōu)化 HBase 客戶端緩存
屬性:hbase.client.write.buffer解釋:默認(rèn)值 2097152bytes(2M)用于指定 HBase 客戶端緩存,增大該值可以減少 RPC調(diào)用次數(shù),但是會(huì)消耗更多內(nèi)存,反之則反之。一般我們需要設(shè)定一定的緩存大小,以達(dá)到減少 RPC 次數(shù)的目的。
2.6 指定 scan.next 掃描 HBase 所獲取的行數(shù)
屬性:hbase.client.scanner.caching解釋:用于指定 scan.next 方法獲取的默認(rèn)行數(shù),值越大,消耗內(nèi)存越大
2.7 BlockCache 占用 RegionServer 堆內(nèi)存的比例
屬性:hfile.block.cache.size解釋:默認(rèn) 0.4,讀請(qǐng)求比較多的情況下,可適當(dāng)調(diào)大
2.8 MemStore 占用 RegionServer 堆內(nèi)存的比例
屬性:hbase.regionserver.global.memstore.size解釋:默認(rèn) 0.4,寫請(qǐng)求較多的情況下,可適當(dāng)調(diào)大
Lars Hofhansl(拉斯·霍夫漢斯)推薦 Region 設(shè)置 20G,刷寫大小設(shè)置 128M,其它默認(rèn)
3、JVM 調(diào)優(yōu)
JVM 調(diào)優(yōu)的思路有兩部分:一是內(nèi)存設(shè)置,二是垃圾回收器設(shè)置。垃圾回收的修改是使用并發(fā)垃圾回收,默認(rèn) PO+PS 是并行垃圾回收,會(huì)有大量的暫停。理由是 HBsae 大量使用內(nèi)存用于存儲(chǔ)數(shù)據(jù),容易遭遇數(shù)據(jù)洪峰造成 OOM,同時(shí)寫緩存的數(shù)據(jù)是不能垃圾回收的,主要回收的就是讀緩存,而讀緩存垃圾回收不影響性能,所以最終設(shè)置的效果可以總結(jié)為:防患于未然,早洗早輕松
# 設(shè)置使用 CMS 收集器(并發(fā))
-XX:+UseConcMarkSweepGC
# 保持新生代盡量小,同時(shí)盡早開啟 GC,例如
# 在內(nèi)存占用到 70%的時(shí)候開啟 GC
-XX:CMSInitiatingOccupancyFraction=70
# 指定使用 70%,不讓 JVM 動(dòng)態(tài)調(diào)整
-XX:+UseCMSInitiatingOccupancyOnly
# 新生代內(nèi)存設(shè)置為 512m
-Xmn512m
# 并行執(zhí)行新生代垃圾回收
-XX:+UseParNewGC
# 設(shè) 置 scanner 掃 描 結(jié) 果 占 用 內(nèi) 存 大 小 , 在 hbase-site.xml 中,設(shè)置
# hbase.client.scanner.max.result.size(默認(rèn)值為 2M)為 eden 空間的 1/8(大概在 64M)
# 設(shè)置多個(gè)與 max.result.size * handler.count 相乘的結(jié)果小于 Survivor Space(新生代經(jīng)過垃圾回收之后存活的對(duì)象)
4、HBase 使用經(jīng)驗(yàn)法則
Region 大小控制 10-50Gcell 大小不超過 10M(性能對(duì)應(yīng)小于 100K 的值有優(yōu)化),如果使用 mob(Medium-sized Objects 一種特殊用法)則不超過 50M1 張表有 1 到 3 個(gè)列族,不要設(shè)計(jì)太多。最好就 1 個(gè),如果使用多個(gè)盡量保證不會(huì)同時(shí)讀取多個(gè)列族1 到 2 個(gè)列族的表格,設(shè)計(jì) 50-100 個(gè) Region列族名稱要盡量短,不要去模仿 RDBMS(關(guān)系型數(shù)據(jù)庫)具有準(zhǔn)確的名稱和描述如果 RowKey 設(shè)計(jì)時(shí)間在最前面,會(huì)導(dǎo)致有大量的舊數(shù)據(jù)存儲(chǔ)在不活躍的 Region中,使用的時(shí)候,僅僅會(huì)操作少數(shù)的活動(dòng) Region,此時(shí)建議增加更多的 Region 個(gè)數(shù)如果只有一個(gè)列族用于寫入數(shù)據(jù),分配內(nèi)存資源的時(shí)候可以做出調(diào)整,即寫緩存不會(huì)占用太多的內(nèi)存
六、整合 Phoenix
1、Phoenix 簡介
1.1 Phoenix 定義
Phoenix 是 HBase 的開源 SQL 皮膚??梢允褂脴?biāo)準(zhǔn) JDBC API 代替 HBase 客戶端 API來創(chuàng)建表,插入數(shù)據(jù)和查詢 HBase 數(shù)據(jù)。
1.2 為什么使用 Phoenix
官方給的解釋為:在 Client 和 HBase 之間放一個(gè) Phoenix 中間層不會(huì)減慢速度,因?yàn)橛脩艟帉懙臄?shù)據(jù)處理代碼和 Phoenix 編寫的沒有區(qū)別(更不用說你寫的垃圾的多),不僅如此Phoenix 對(duì)于用戶輸入的 SQL 同樣會(huì)有大量的優(yōu)化手段(就像 hive 自帶 sql 優(yōu)化器一樣)。
Phoenix 在 5.0 版本默認(rèn)提供有兩種客戶端使用(瘦客戶端和胖客戶端),在 5.1.2 版本安裝包中刪除了瘦客戶端,本文也不再使用瘦客戶端。而胖客戶端和用戶自己寫 HBase 的API 代碼讀取數(shù)據(jù)之后進(jìn)行數(shù)據(jù)處理是完全一樣的
2、Phoenix快速入門
官網(wǎng):https://phoenix.apache.org/
2.1 Phoenix安裝部署
wget http://archive.apache.org/dist/phoenix/phoenix-5.1.2/phoenix-hbase-2.4-5.1.2-bin.tar.gz
tar -zxvf phoenix-hbase-2.4-5.1.2-bin.tar.gz -C /opt/module/
mv phoenix-hbase-2.4-5.1.2-bin/ phoenix
# 復(fù)制 server 包并拷貝到各個(gè)節(jié)點(diǎn)的 hbase/lib
cd /opt/module/phoenix/
cp phoenix-server-hbase-2.4-5.1.2.jar /opt/module/hbase/lib/
xsync /opt/module/hbase/lib/phoenix-server-hbase-2.4-5.1.2.jar
# 配置環(huán)境變量
vim /etc/profile.d/my_env.sh
#phoenix
export PHOENIX_HOME=/opt/module/phoenix
export PHOENIX_CLASSPATH=$PHOENIX_HOME
export PATH=$PATH:$PHOENIX_HOME/bin
# 刷新環(huán)境變量
source /etc/profile.d/my_env.sh
# 重啟 HBase
stop-hbase.sh
start-hbase.sh
# 連接 Phoenix
/opt/module/phoenix/bin/sqlline.py hadoop102,hadoop103,hadoop104:2181
# 錯(cuò)誤解決
# 出現(xiàn)下面錯(cuò)誤的原因是之前使用過 phoenix,建議刪除之前的記錄
# 警告: Failed to load history
# java.lang.IllegalArgumentException: Bad history file syntax! The
# history file `/home/atguigu/.sqlline/history` may be an older
# history: please remove it or use a different history file.
# 解決方法:在/home/atguigu 目錄下刪除.sqlline 文件夾
rm -rf .sqlline/
2.2 Phoenix Shell 操作
table
建議查看官網(wǎng):https://phoenix.apache.org/language/index.html
# 顯示所有表
!table 或 !tables
# 創(chuàng)建表
# 直接指定單個(gè)列作為 RowKey
CREATE TABLE IF NOT EXISTS student(
id VARCHAR primary key,
name VARCHAR,
age BIGINT,
addr VARCHAR);
# 在 phoenix 中,表名等會(huì)自動(dòng)轉(zhuǎn)換為大寫,若要小寫,使用雙引號(hào),如"us_population"。
# 指定多個(gè)列的聯(lián)合作為 RowKey
CREATE TABLE IF NOT EXISTS student1 (
id VARCHAR NOT NULL,
name VARCHAR NOT NULL,
age BIGINT,
addr VARCHAR
CONSTRAINT my_pk PRIMARY KEY (id, name));
# 注:Phoenix 中建表,會(huì)在 HBase 中創(chuàng)建一張對(duì)應(yīng)的表。為了減少數(shù)據(jù)對(duì)磁盤空間的占,Phoenix 默認(rèn)會(huì)對(duì) HBase 中的列名做編碼處理。
# 具體規(guī)則可參考官網(wǎng)鏈接:https://phoenix.apache.org/columnencoding.html,若不想對(duì)列名編碼,可在建表語句末尾加上 COLUMN_ENCODED_BYTES = 0;
# 插入數(shù)據(jù)
upsert into student values('1001','zhangsan', 10, 'beijing');
# 查詢記錄
select * from student;
select * from student where id='1001';
# 刪除記錄
delete from student where id='1001';
# 刪除表
drop table student;
# 退出命令行
!quit
表的映射
默認(rèn)情況下,HBase 中已存在的表,通過 Phoenix 是不可見的。如果要在 Phoenix 中操作 HBase 中已存在的表,可以在 Phoenix 中進(jìn)行表的映射。映射方式有兩種:視圖映射和表映射
# 啟動(dòng)
/opt/module/hbase/bin/hbase shell
# 創(chuàng)建 HBase 表 test
create 'test','info1','info2'
# 視圖映射
# Phoenix 創(chuàng)建的視圖是只讀的,所以只能用來做查詢,無法通過視圖對(duì)數(shù)據(jù)進(jìn)行修改等操作
create view "test"(id varchar primary key,"info1"."name" varchar, "info2"."address" varchar);
# 刪除視圖
drop view "test";
# 表映射
# 在 Pheonix 創(chuàng)建表去映射 HBase 中已經(jīng)存在的表,是可以修改刪除 HBase 中已經(jīng)存在的數(shù)據(jù)的
# 刪除 Phoenix 中的表,那么 HBase 中被映射的表也會(huì)被刪除
# 注:進(jìn)行表映射時(shí),不能使用列名編碼,需將 column_encoded_bytes 設(shè)為 0
create table "test"(id varchar primary key,"info1"."name" varchar, "info2"."address" varchar) column_encoded_bytes=0;
數(shù)字類型說明
HBase 中的數(shù)字,底層存儲(chǔ)為補(bǔ)碼,而 Phoenix 中的數(shù)字,底層存儲(chǔ)為在補(bǔ)碼的基礎(chǔ)上,將符號(hào)位反轉(zhuǎn)。故當(dāng)在 Phoenix 中建表去映射 HBase 中已存在的表,當(dāng) HBase 中有數(shù)字類型的字段時(shí),會(huì)出現(xiàn)解析錯(cuò)誤的現(xiàn)象
# Hbase 演示
create 'test_number','info'
put 'test_number','1001','info:number',Bytes.toBytes(1000)
scan 'test_number',{COLUMNS => 'info:number:toLong'}
# phoenix 演示:
create view "test_number"(id varchar primary key,"info"."number" bigint);
select * from "test_number";
解決方法:
(1)Phoenix 種提供了 unsigned_int,unsigned_long 等無符號(hào)類型,其對(duì)數(shù)字的編碼解碼方式和 HBase 是相同的,如果無需考慮負(fù)數(shù),那在 Phoenix 中建表時(shí)采用無符號(hào)類型是最合適的選擇
drop view "test_number";
create view "test_number"(id varchar primary key,"info"."number" unsigned_long);
select * from "test_number";
(2)如需考慮負(fù)數(shù)的情況,則可通過 Phoenix 自定義函數(shù),將數(shù)字類型的最高位,即符號(hào)位反轉(zhuǎn)即可,自定義函數(shù)可參考如下鏈接:https://phoenix.apache.org/udf.html
2.3 Phoenix JDBC 操作
此處演示一個(gè)標(biāo)準(zhǔn)的 JDBC 連接操作,實(shí)際開發(fā)中會(huì)直接使用別的框架內(nèi)嵌的 Phoenix 連接,胖客戶端加入maven依賴
編寫代碼
public class PhoenixClient {
public static void main(String[] args) throws SQLException {
// 標(biāo)準(zhǔn)的 JDBC 代碼
// 1.添加鏈接
String url = "jdbc:phoenix:hadoop102,hadoop103,hadoop104:2181";
// 2. 創(chuàng)建配置
// 沒有需要添加的必要配置 因?yàn)?Phoenix 沒有賬號(hào)密碼
Properties properties = new Properties();
// 3. 獲取連接
Connection connection = DriverManager.getConnection(url,properties);
// 5.編譯 SQL 語句
PreparedStatement preparedStatement = connection.prepareStatement("select * from student");
// 6.執(zhí)行語句
ResultSet resultSet = preparedStatement.executeQuery();
// 7.輸出結(jié)果
while (resultSet.next()){
System.out.println(resultSet.getString(1) + ":" +
resultSet.getString(2) + ":" + resultSet.getString(3));
}
// 8.關(guān)閉資源
connection.close();
// 由于 Phoenix 框架內(nèi)部需要獲取一個(gè) HBase 連接,所以會(huì)延遲關(guān)閉
// 不影響后續(xù)的代碼執(zhí)行
System.out.println("hello");
}
}
3、Phoenix 二級(jí)索引
3.1 二級(jí)索引配置文件
添加如下配置到 HBase 的 HRegionserver 節(jié)點(diǎn)的 hbase-site.xml
3.2 全局索引(global index)
Global Index 是默認(rèn)的索引格式,創(chuàng)建全局索引時(shí),會(huì)在 HBase 中建立一張新表。也就是說索引數(shù)據(jù)和數(shù)據(jù)表是存放在不同的表中的,因此全局索引適用于多讀少寫的業(yè)務(wù)場景。
寫數(shù)據(jù)的時(shí)候會(huì)消耗大量開銷,因?yàn)樗饕硪惨拢饕硎欠植荚诓煌臄?shù)據(jù)節(jié)點(diǎn)上的,跨節(jié)點(diǎn)的數(shù)據(jù)傳輸帶來了較大的性能消耗。在讀數(shù)據(jù)的時(shí)候 Phoenix 會(huì)選擇索引表來降低查詢消耗的時(shí)間
# 創(chuàng)建單個(gè)字段的全局索引
CREATE INDEX my_index ON my_table (my_col);
#例如
create index my_index on student1(age);
#刪除索引
DROP INDEX my_index ON my_table
drop index my_index on student1;
# 查看二級(jí)索引是否有效,可以使用 explainPlan 執(zhí)行計(jì)劃,有二級(jí)索引之后會(huì)變成范圍掃描
explain select id,name from student1 where age = 10;
# 如果想查詢的字段不是索引字段的話索引表不會(huì)被使用,也就是說不會(huì)帶來查詢速度的提升
explain select
id,name,addr from student1 where age = 10;
3.3 包含索引(covered index)
# 創(chuàng)建攜帶其他字段的全局索引(本質(zhì)還是全局索引)
CREATE INDEX my_index ON my_table (v1) INCLUDE (v2);
# 先刪除之前的索引
drop index my_index on student1;
# 創(chuàng)建包含索引
create index my_index on student1(age) include (addr);
# 之后使用執(zhí)行計(jì)劃查看效果,使用explain
3.4 本地索引(local index)
Local Index 適用于寫操作頻繁的場景。索引數(shù)據(jù)和數(shù)據(jù)表的數(shù)據(jù)是存放在同一張表中(且是同一個(gè) Region),避免了在寫操作的時(shí)候往不同服務(wù)器的索引表中寫索引帶來的額外開銷
# my_column 可以是多個(gè)
CREATE LOCAL INDEX my_index ON my_table (my_column);
# 本地索引會(huì)將所有的信息存在一個(gè)影子列族中,雖然讀取的時(shí)候也是范圍掃描,但是沒有全局索引快,優(yōu)點(diǎn)在于不用寫多個(gè)表了
#刪除之前的索引
drop index my_index on student1;
#創(chuàng)建本地索引
CREATE LOCAL INDEX my_index ON student1 (age,addr);
#使用執(zhí)行計(jì)劃
explain select id,name,addr from student1 where age = 10;
七、Hive集成
1、集成使用
1.1 使用場景
如果大量的數(shù)據(jù)已經(jīng)存放在 HBase 上面,需要對(duì)已經(jīng)存在的數(shù)據(jù)進(jìn)行數(shù)據(jù)分析處理,那么 Phoenix 并不適合做特別復(fù)雜的 SQL 處理,此時(shí)可以使用 hive 映射 HBase 的表格,之后寫 HQL 進(jìn)行分析處理。
1.2 配置
在 hive-site.xml 中添加 zookeeper 的屬性
2、案例舉例
2.1 案例一
目標(biāo):建立 Hive 表,關(guān)聯(lián) HBase 表,插入數(shù)據(jù)到 Hive 表的同時(shí)能夠影響 HBase 表。
# 在 Hive 中創(chuàng)建表同時(shí)關(guān)聯(lián) HBase
CREATE TABLE hive_hbase_emp_table(
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
comm double,
deptno int
)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,info:ename,info:job,info:mgr,info:hiredate,info:sal,info:comm,info:deptno")
TBLPROPERTIES ("hbase.table.name" = "hbase_emp_table");
# 提示:完成之后,可以分別進(jìn)入 Hive 和 HBase 查看,都生成了對(duì)應(yīng)的表
# (2)在 Hive 中創(chuàng)建臨時(shí)中間表,用于 load 文件中的數(shù)據(jù)
# 提示:不能將數(shù)據(jù)直接 load 進(jìn) Hive 所關(guān)聯(lián) HBase 的那張表中
CREATE TABLE emp(
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
comm double,
deptno int
)
row format delimited fields terminated by '\t';
# (3)向 Hive 中間表中 load 數(shù)據(jù)
load data local inpath '/opt/software/emp.txt' into table emp;
# (4)通過 insert 命令將中間表中的數(shù)據(jù)導(dǎo)入到 Hive 關(guān)聯(lián) Hbase 的那張表中
insert into table hive_hbase_emp_table select * from emp;
# 查看 Hive 以及關(guān)聯(lián)的 HBase 表中是否已經(jīng)成功的同步插入了數(shù)據(jù)
hive> select * from hive_hbase_emp_table;
Hbase> scan 'hbase_emp_table'
2.2 案例二
目標(biāo):在 HBase 中已經(jīng)存儲(chǔ)了某一張表 hbase_emp_table,然后在 Hive 中創(chuàng)建一個(gè)外部表來關(guān)聯(lián) HBase 中的 hbase_emp_table 這張表,使之可以借助 Hive 來分析 HBase 這張表中的數(shù)據(jù)。
# 在 Hive 中創(chuàng)建外部表
CREATE EXTERNAL TABLE relevance_hbase_emp(
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
deptno int
)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,info:ename,info:job,info:mgr,info:hiredate,info:sal,info:comm,info:deptno")
TBLPROPERTIES ("hbase.table.name" = "hbase_emp_table");
# 關(guān)聯(lián)后就可以使用 Hive 函數(shù)進(jìn)行一些分析操作了
hive (default)> select deptno,avg(sal) monery from relevance_hbase_emp group by deptno;
柚子快報(bào)邀請(qǐng)碼778899分享:HBase2.x學(xué)習(xí)筆記
參考鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。