欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報邀請碼778899分享:HBase2.x學(xué)習(xí)筆記

柚子快報邀請碼778899分享:HBase2.x學(xué)習(xí)筆記

http://yzkb.51969.com/

文章目錄

一、HBase 簡介1、HBase 定義1.1 概述1.2 HBase 與 Hadoop 的關(guān)系1.3 RDBMS 與 HBase 的對比1.4 HBase 特征簡要

2、HBase 數(shù)據(jù)模型2.1 HBase 邏輯結(jié)構(gòu)2.2 HBase 物理存儲結(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ā)與啟動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è)計1.1 實(shí)現(xiàn)需求 11.2 實(shí)現(xiàn)需求 21.3 添加預(yù)分區(qū)優(yōu)化

2、參數(shù)優(yōu)化2.1 Zookeeper 會話超時時間2.2 設(shè)置 RPC 監(jiān)聽數(shù)量2.3 手動控制 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 二級索引3.1 二級索引配置文件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 之上,提供高可靠性、高性能、列存儲、可伸縮、實(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ò)展,通過不斷增加廉價的商用服務(wù)器,來增加計算和存儲能力。HBase 中的表一般有這樣的特點(diǎn):

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

1.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)化了多次讀,以及多次寫

1.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 查詢面向行,即每一行都是一個連續(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ù)面向列,即每一列都是一個連續(xù)的單元數(shù)據(jù)總量不依賴具體某臺機(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 特征簡要

海量存儲

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

列式存儲

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

極易擴(kuò)展

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

高并發(fā)

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

稀疏

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

2、HBase 數(shù)據(jù)模型

HBase 的設(shè)計理念依據(jù) Google 的 BigTable 論文,論文中對于數(shù)據(jù)模型的首句介紹:Bigtable 是一個稀疏的、分布式的、持久的多維排序 map。之后對于映射的解釋如下:該映射由行鍵、列鍵和時間戳索引;映射中的每個值都是一個未解釋的字節(jié)數(shù)組。最終 HBase 關(guān)于數(shù)據(jù)模型和 BigTable 的對應(yīng)關(guān)系如下:HBase 使用與 Bigtable 非常相似的數(shù)據(jù)模型。用戶將數(shù)據(jù)行存儲在帶標(biāo)簽的表中。數(shù)據(jù)行具有可排序的鍵和任意數(shù)量的列。該表存儲稀疏,因此如果用戶喜歡,同一表中的行可以具有瘋狂變化的列。

最終理解 HBase 數(shù)據(jù)模型的關(guān)鍵在于稀疏、分布式、多維、排序的映射。其中映射 map指代非關(guān)系型數(shù)據(jù)庫的 key-Value 結(jié)構(gòu)

2.1 HBase 邏輯結(jié)構(gòu)

HBase 可以用于存儲多種結(jié)構(gòu)的數(shù)據(jù),以 JSON 為例,存儲的數(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 物理存儲結(jié)構(gòu)

物理存儲結(jié)構(gòu)即為數(shù)據(jù)映射關(guān)系,而在概念視圖的空單元格,底層實(shí)際根本不存儲

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

Name Space

命名空間,類似于關(guān)系型數(shù)據(jù)庫的 database 概念,每個命名空間下有多個表。HBase 兩個自帶的命名空間,分別是 hbase 和 default,hbase 中存放的是 HBase 內(nèi)置的表,default表是用戶默認(rèn)使用的命名空間

Table

類似于關(guān)系型數(shù)據(jù)庫的表概念。不同的是,HBase 定義表時只需要聲明列族即可,不需要聲明具體的列。因?yàn)閿?shù)據(jù)存儲時稀疏的,所有往 HBase 寫入數(shù)據(jù)時,字段可以動態(tài)、按需指定。因此,和關(guān)系型數(shù)據(jù)庫相比,HBase 能夠輕松應(yīng)對字段變更的場景

行鍵 Row Key

HBase 表中的每行數(shù)據(jù)都由一個 RowKey 和多個 Column(列)組成,數(shù)據(jù)是按照 RowKey 的字典順序存儲的,并且查詢數(shù)據(jù)時只能根據(jù) RowKey 進(jìn)行檢索,所以 RowKey 的設(shè)計十分重要。與nosql數(shù)據(jù)庫一樣,row key是用來檢索記錄的主鍵。訪問hbase table中的行,只有三種方式:

通過單個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ù)據(jù)按照Row key的字典序(byte order)排序存儲。設(shè)計key時,要充分排序存儲這個特性,將經(jīng)常一起讀取的行存儲放到一起。(位置相關(guān)性)。

注意: 字典序?qū)nt排序的結(jié)果是 1,10,100,11,12,13,14,15,16,17,18,19,2,20,21 … 。要保持整形的自然序,行鍵必須用0作左填充。行的一次讀寫是原子操作 (不論一次讀寫多少列)。這個設(shè)計決策能夠使用戶很容易的理解程序在對同一個行進(jìn)行并發(fā)更新操作時的行為。

列族 Column Family

HBase表中的每個列,都?xì)w屬于某個列族。列族是表的schema的一部分(而列不是),必須在使用表之前定義。列名都以列族作為前綴。例如 courses:history , courses:math 都屬于 courses 這個列族。

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

列 Column

HBase 中的每個列都由 **Column Family(列族)**和 **Column Qualifier(列限定符)**進(jìn)行限定,例如 info:name,info:age。建表時,只需指明列族,而列限定符無需預(yù)先定義

時間戳 Timestamp

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

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

用戶可以針對每個列族進(jìn)行設(shè)置

單元 Cell

由{rowkey, column Family:column Qualifier, timestamp} 唯一確定的單元。cell 中的數(shù)據(jù)全部是字節(jié)碼形式存貯

版本號 VersionNum

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

3、HBase 基本架構(gòu)

3.1 Master

實(shí)現(xiàn)類為 HMaster,負(fù)責(zé)監(jiān)控集群中所有的 RegionServer 實(shí)例。主要作用如下:

管理元數(shù)據(jù)表格 hbase:meta,接收用戶對表格創(chuàng)建修改刪除的命令并執(zhí)行監(jiān)控 region 是否需要進(jìn)行負(fù)載均衡,故障轉(zhuǎn)移和 region 的拆分;通過啟動多個后臺線程監(jiān)控實(shí)現(xiàn)上述功能:

LoadBalancer 負(fù)載均衡器:周期性監(jiān)控 region 分布在 regionServer 上面是否均衡,由參數(shù) hbase.balancer.period 控制周期時間,默認(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 的分配或移除在空閑時間進(jìn)行數(shù)據(jù)的負(fù)載均衡通過 Zookeeper 發(fā)布自己的位置給客戶端

3.2 Region Server

Region Server 實(shí)現(xiàn)類為 HRegionServer,主要作用如下:

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

幾個組件如下

Write-Ahead logs

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

HFile

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

Store

HFile 存儲在 Store 中,一個 Store 對應(yīng) HBase 表中的一個列族。

MemStore

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

Region

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

3.3 Zookeeper

HBase 通過 Zookeeper 來做 master 的高可用、記錄 RegionServer 的部署信息、并且存儲有 meta 表的位置信息。

HBase 對于數(shù)據(jù)的讀寫操作時直接訪問 Zookeeper 的,在 2.3 版本推出 Master Registry模式,客戶端可以直接訪問 master。使用此功能,會加大對 master 的壓力,減輕對 Zookeeper的壓力。

3.4 HDFS

HDFS 為 Hbase 提供最終的底層數(shù)據(jù)存儲服務(wù),同時為 HBase 提供高容錯的支持

二、HBase 快速入門

1、HBase 安裝部署

1.1 前置環(huán)境與下載

# HBase啟動需要有zookeeper和hadoop,這個可以參考之前的文章

# 三臺服務(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

hbase.zookeeper.quorum

hadoop102,hadoop103,hadoop104

The directory shared by RegionServers.

hbase.rootdir

hdfs://hadoop102:8020/hbase

The directory shared by RegionServers.

hbase.cluster.distributed

true

然后修改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ā)與啟動

# 分發(fā)hbase配置文件

xsync /opt/module/hbase/

# HBase 服務(wù)的啟動

bin/hbase-daemon.sh start master

bin/hbase-daemon.sh start regionserver

# 群啟

bin/start-hbase.sh

bin/stop-hbase.sh

# 啟動成功后,可以通過“host:port”的方式來訪問 HBase 管理頁面

# http://hadoop102:16010

1.4 高可用(可選,推薦)

在 HBase 中 HMaster 負(fù)責(zé)監(jiān)控 HRegionServer 的生命周期,均衡 RegionServer 的負(fù)載, 如果HMaster 掛掉了,那么整個 HBase 集群將陷入不健康的狀態(tài),并且此時的工作狀態(tài)并不會維持太久。所以HBase 支持對 HMaster 的高可用配置

# 關(guān)閉 HBase 集群(如果沒有開啟則跳過此步)

bin/stop-hbase.sh

# 在 conf 目錄下創(chuàng)建 backup-masters 文件

touch conf/backup-masters

# 在 backup-masters 文件中配置高可用 HMaster 節(jié)點(diǎn),注意可以寫多個

echo hadoop103 > conf/backup-masters

# 將整個 conf 目錄 scp 到其他節(jié)點(diǎn)

xsync conf

# 重啟 hbase,打開頁面測試查看

bin/start-hbase.sh

bin/stop-hbase.sh

# 宕機(jī)機(jī)器開啟,但也只會變成backup,注意,如果要停止的話需要進(jìn)入master那臺機(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,兩個列族。info 列族數(shù)據(jù)維護(hù)的版本數(shù)為 5 個,如果不寫默認(rèn)版本數(shù)為 1

hbase:005:0> create 'bigdata:student', {NAME => 'info', VERSIONS => 5}, {NAME => 'msg'}

# 如果創(chuàng)建表格只有一個列族,沒有列族屬性,可以簡寫

# 如果不寫命名空間,使用默認(rèn)的命名空間 default

hbase:009:0> create 'student1','info'

# 查看表

# 查看表有兩個命令:list 和 describe

hbase:013:0> list

# describe:查看一個表的詳情

hbase:014:0> describe 'student1'

# 修改表

# 表名創(chuàng)建時寫的所有和列族相關(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??梢允謩訉懭霑r間戳指定 cell 的版本,推薦不寫默認(rèn)使用當(dāng)前的系統(tǒng)時間。

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ù),會寫入多個版本進(jìn)行覆蓋

# 讀取數(shù)據(jù)

# 讀取數(shù)據(jù)的方法有兩個: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)讀取一個。最多能夠讀取當(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ī)會不多,所有豐富的使用方法到 API 中介紹

# 刪除數(shù)據(jù)

# 刪除數(shù)據(jù)的方法有兩個:delete 和 deleteall

# delete 表示刪除一個版本的數(shù)據(jù),即為 1 個 cell,不填寫版本默認(rèn)刪除最新的一個版本

hbase:026:0> delete 'bigdata:student','1001','info:name'

# deleteall 表示刪除所有版本的數(shù)據(jù),即為當(dāng)前行當(dāng)前列的多個 cell。(執(zhí)行命令會標(biāo)記數(shù)據(jù)為要刪除,不會直接將數(shù)據(jù)徹底刪除,刪除數(shù)據(jù)只在特定時期清理磁盤時進(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)計指定表的記錄數(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)備

新建項目后在 pom.xml 中添加依賴,注意:會報錯 javax.el 包不存在,是一個測試用的依賴,不影響使用

org.apache.hbase

hbase-server

2.4.11

org.glassfish

javax.el

org.glassfish

javax.el

3.0.1-b06

1.2 單線程連接

HBase 的客戶端連接由 ConnectionFactory 類來創(chuàng)建,用戶使用完成之后需要手動關(guān)閉連接。同時連接是一個重量級的,推薦一個進(jìn)程使用一個連接,對 HBase 的命令通過連接中的兩個屬性 Admin 和 Table 來實(shí)現(xiàn)

public class HBaseConnect {

public static void main(String[] args) throws IOException {

// 1. 創(chuàng)建配置對象

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 asyncConnection =

ConnectionFactory.createAsyncConnection(conf);

// 4. 使用連接

System.out.println(connection);

// 5. 關(guān)閉連接

connection.close();

}

}

1.3 多線程創(chuàng)建連接(推薦)

使用類單例模式,確保使用一個連接,可以同時用于多個線程

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)閉時調(diào)用

*

* @throws IOException

*/

public static void closeConnection() throws IOException {

if (connection != null) {

connection.close();

}

}

}

在 resources 文件夾中創(chuàng)建配置文件 hbase-site.xml,添加以下內(nèi)容

hbase.zookeeper.quorum

hadoop102,hadoop103,hadoop104

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 的連接是輕量級的 不是線程安全的 不推薦池化或者緩存這個連接

Admin admin = connection.getAdmin();

// 2. 調(diào)用方法創(chuàng)建命名空間

// 代碼相對 shell 更加底層 所以 shell 能夠?qū)崿F(xiàn)的功能 代碼一定能實(shí)現(xiàn)

// 所以需要填寫完整的命名空間描述

// 2.1 創(chuàng)建命令空間描述建造者 => 設(shè)計師

NamespaceDescriptor.Builder builder =

NamespaceDescriptor.create(namespace);

// 2.2 給命令空間添加需求

builder.addConfiguration("user", "atguigu");

// 2.3 使用 builder 構(gòu)造出對應(yīng)的添加完參數(shù)的對象 完成創(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 列族名稱 可以有多個

*/

public static void createTable(String namespace , String

tableName , String... columnFamilies) throws IOException {

// 判斷是否有至少一個列族

if (columnFamilies.length == 0){

System.out.println("創(chuàng)建表格至少有一個列族");

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 對應(yīng)當(dāng)前的列族添加參數(shù)

// 添加版本參數(shù)

columnFamilyDescriptorBuilder.setMaxVersions(5);

// 2.5 創(chuàng)建添加完參數(shù)的列族描述

tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptorBuilder.build());

}

// 2.6 創(chuàng)建對應(yīng)的表格描述

try {

admin.createTable(tableDescriptorBuilder.build());

} catch (IOException e) {

e.printStackTrace();

}

// 3. 關(guān)閉 admin

admin.close();

}

2.4 修改表

/**

* 修改表格中一個列族的版本

*

* @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)建一個表格描述建造者

// 如果使用填寫 tableName 的方法 相當(dāng)于創(chuàng)建了一個新的表格描述建造者沒有之前的信息

// 如果想要修改之前的信息 必須調(diào)用方法填寫一個舊的表格描述

TableDescriptorBuilder tableDescriptorBuilder =

TableDescriptorBuilder.newBuilder(descriptor);

// 2.2 對應(yīng)建造者進(jìn)行表格數(shù)據(jù)的修改

ColumnFamilyDescriptor columnFamily1 =

descriptor.getColumnFamily(Bytes.toBytes(columnFamily));

// 創(chuàng)建列族描述建造者

// 需要填寫舊的列族描述

ColumnFamilyDescriptorBuilder

columnFamilyDescriptorBuilder = ColumnFamilyDescriptorBuilder.newBuilder(columnFamily1);

// 修改對應(yīng)的版本

columnFamilyDescriptorBuilder.setMaxVersions(version);

// 此處修改的時候 如果填寫的新創(chuàng)建 那么別的參數(shù)會初始化

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 對象

Put put = new Put(Bytes.toBytes(rowKey));

// 2.2. 給 put 對象添加數(shù)據(jù)

put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnName), Bytes.toBytes(value));

// 2.3 將對象寫入對應(yīng)的方法

try {

table.put(put);

} catch (IOException e) {

e.printStackTrace();

}

// 3. 關(guān)閉 table

table.close();

}

3.2 讀取數(shù)據(jù)

/**

* 讀取數(shù)據(jù) 讀取對應(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 對象

Get get = new Get(Bytes.toBytes(rowKey));

// 如果直接調(diào)用 get 方法讀取數(shù)據(jù) 此時讀一整行數(shù)據(jù)

// 如果想讀取某一列的數(shù)據(jù) 需要添加對應(yīng)的參數(shù)

get.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnName));

// 設(shè)置讀取數(shù)據(jù)的版本

get.readAllVersions();

try {

// 讀取數(shù)據(jù) 得到 result 對象

Result result = table.get(get);

// 處理數(shù)據(jù)

Cell[] cells = result.rawCells();

// 測試方法: 直接把讀取的數(shù)據(jù)打印到控制臺

// 如果是實(shí)際開發(fā) 需要再額外寫方法 對應(yīng)處理數(shù)據(jù)

for (Cell cell : cells) {

// cell 存儲數(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 對象

Scan scan = new Scan();

// 如果此時直接調(diào)用 會直接掃描整張表

// 添加參數(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 對象

Scan scan = new Scan();

// 如果此時直接調(diào)用 會直接掃描整張表

// 添加參數(shù) 來控制掃描的數(shù)據(jù)

// 默認(rèn)包含

scan.withStartRow(Bytes.toBytes(startRow));

// 默認(rèn)不包含

scan.withStopRow(Bytes.toBytes(stopRow));

// 可以添加多個過濾

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é)果同時會保留沒有當(dāng)前列的數(shù)據(jù)

SingleColumnValueFilter singleColumnValueFilter = new

SingleColumnValueFilter(

// 列族名稱

Bytes.toBytes(columnFamily),

// 列名

Bytes.toBytes(columnName),

// 比較關(guān)系

CompareOperator.EQUAL,

// 值

Bytes.toBytes(value)

);

// 本身可以添加多個過濾器

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 對象

Delete delete = new Delete(Bytes.toBytes(rowKey));

// 3.添加刪除信息

// 3.1 刪除單個版本

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 表格介紹:(警告:不要去改這個表),全稱 hbase:meta,只是在 list 命令中被過濾掉了,本質(zhì)上和 HBase 的其他表格一樣。

RowKey:

([table],[region start key],[region id]) 即 表名,region 起始位置和 regionID

列:

info:regioninfo 為 region 信息,存儲一個 HRegionInfo 對象info:server 當(dāng)前 region 所處的 RegionServer 信息,包含端口號info:serverstartcode 當(dāng)前 region 被分到 RegionServer 的起始時間

如果一個表處于切分的過程中,即 region 切分,還會多出兩列 info:splitA 和 info:splitB,存儲值也是 HRegionInfo 對象,拆分結(jié)束后,刪除這兩列。

注意:在客戶端對元數(shù)據(jù)進(jìn)行操作的時候才會連接 master,如果對數(shù)據(jù)進(jìn)行讀寫,直接連接zookeeper 讀取目錄/hbase/meta-region-server 節(jié)點(diǎn)信息,會記錄 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更新請求

1.2 RegionServer 架構(gòu)

HRegion 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)

一些組件介紹

MemStore

寫緩存,由于 HFile 中的數(shù)據(jù)要求是有序的,所以數(shù)據(jù)是先存儲在 MemStore 中,排好序后,等到達(dá)刷寫時機(jī)才會刷寫到 HFile,每次刷寫都會形成一個新的 HFile,寫入到對應(yīng)的文件夾 store 中。

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

WAL

由于數(shù)據(jù)要經(jīng) MemStore 排序后才能刷寫到 HFile,但把數(shù)據(jù)保存在內(nèi)存中會有很高的概率導(dǎo)致數(shù)據(jù)丟失,為了解決這個問題,數(shù)據(jù)會先寫在一個叫做 Write-Ahead logfile 的文件中,然后再寫入 MemStore 中。所以在系統(tǒng)出現(xiàn)故障的時候,數(shù)據(jù)可以通過這個日志文件重建

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

BlockCache

讀緩存,每次查詢出的數(shù)據(jù)會緩存在 BlockCache 中,方便下次查詢

2、HRegion管理和HMaster工作機(jī)制

2.1 HRegion管理

HRegion分配

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

HRegion Server上線

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

HRegion Server下線

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

HRegion Server和zookeeper之間的網(wǎng)絡(luò)斷開了。HRegion Server掛了。

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

2.2 HMaster工作機(jī)制

master上線

master啟動進(jìn)行以下步驟:

從zookeeper上獲取唯一一個代表active master的鎖,用來阻止其它HMaster成為master。掃描zookeeper上的server父節(jié)點(diǎn),獲得當(dāng)前可用的HRegion Server列表。和每個HRegion Server通信,獲得當(dāng)前已分配的HRegion和HRegion Server的對應(yīng)關(guān)系。掃描.META.region的集合,計算得到當(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下線短時間內(nèi)對整個HBase集群沒有影響。

從上線過程可以看到,HMaster保存的信息全是可以冗余信息(都可以從系統(tǒng)其它地方收集到或者計算出來)因此,一般HBase集群中總是有一個HMaster在提供服務(wù),還有一個以上的‘HMaster’在等待時機(jī)搶占它的位置

3、寫流程

寫流程順序正如 API 編寫順序,首先創(chuàng)建 HBase 的重量級連接

首先訪問 zookeeper,獲取 hbase:meta 表位于哪個 Region Server;訪問對應(yīng)的 Region Server,獲取 hbase:meta 表,將其緩存到連接中,作為連接屬性 MetaCache,由于 Meta 表格具有一定的數(shù)據(jù)量,導(dǎo)致了創(chuàng)建連接比較慢;之后使用創(chuàng)建的連接獲取 Table,這是一個輕量級的連接,只有在第一次創(chuàng)建的時候會檢查表格是否存在訪問 RegionServer,之后在獲取 Table 時不會訪問 RegionServer;調(diào)用Table的put方法寫入數(shù)據(jù),此時還需要解析RowKey,對照緩存的MetaCache,查看具體寫入的位置有哪個 RegionServer;將數(shù)據(jù)順序?qū)懭耄ㄗ芳樱┑?WAL,此處寫入是直接落盤的,并設(shè)置專門的線程控制 WAL 預(yù)寫日志的滾動(類似 Flume);根據(jù)寫入命令的 RowKey 和 ColumnFamily 查看具體寫入到哪個 MemStory,并且在 MemStory 中排序;向客戶端發(fā)送 ack;等達(dá)到 MemStore 的刷寫時機(jī)后,將數(shù)據(jù)刷寫到對應(yīng)的 story 中

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

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

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

4、MemStore Flush

MemStore 刷寫由多個線程控制,條件互相獨(dú)立,主要的刷寫規(guī)則是控制刷寫文件的大小,在每一個刷寫線程中都會進(jìn)行監(jiān)控

刷寫規(guī)則一

當(dāng)某個 memstroe 的大小達(dá)到了 hbase.hregion.memstore.flush.size(默認(rèn)值 128M),其所在 region 的所有 memstore 都會刷寫。當(dāng) memstore 的大小達(dá)到了

hbase.hregion.memstore.flush.size(默認(rèn)值 128M)hbase.hregion.memstore.block.multiplier(默認(rèn)值 4)

時,會刷寫同時阻止繼續(xù)往該 memstore 寫數(shù)據(jù)(由于線程監(jiān)控是周期性的,所有有可能面對數(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)存存儲限制而阻塞更新時,會發(fā)生最小可能的刷新)

region 會按照其所有 memstore 的大小順序(由大到?。┮来芜M(jìn)行刷寫。直到 region server中所有 memstore 的總大小減小到上述值以下

當(dāng) region server 中 memstore 的總大小達(dá)到高水位線:

java_heapsizehbase.regionserver.global.memstore.size(默認(rèn)值 0.4)

時,會同時阻止繼續(xù)往所有的 memstore 寫數(shù)據(jù)

刷寫規(guī)則三

為了避免數(shù)據(jù)過長時間處于內(nèi)存之中,到達(dá)自動刷寫的時間,也會觸發(fā) memstoreflush。由 HRegionServer 的屬性 PeriodicMemStoreFlusher 控制進(jìn)行,由于重要性比較低,5min才會執(zhí)行一次。自動刷新的時間間隔由該屬性進(jìn)行配置hbase.regionserver.optionalcacheflushinterval(默認(rèn)1 小時)

刷寫規(guī)則四

當(dāng) WAL 文件的數(shù)量超過 hbase.regionserver.max.logs,region 會按照時間順序依次進(jìn)行刷寫,直到 WAL 文件數(shù)量減小到 hbase.regionserver.max.log 以下(該屬性名已經(jīng)廢棄,現(xiàn)無需手動設(shè)置,最大值為 32)

5、讀流程

5.1 HFile 結(jié)構(gòu)

HFile 是存儲在 HDFS 上面每一個 store 文件夾下實(shí)際存儲數(shù)據(jù)的文件。里面存儲多種內(nèi)容。包括數(shù)據(jù)本身(keyValue 鍵值對)、元數(shù)據(jù)記錄、文件信息、數(shù)據(jù)索引、元數(shù)據(jù)索引和一個固定長度的尾部信息(記錄文件的修改情況)。

鍵值對按照塊大小(默認(rèn) 64K)保存在文件中,數(shù)據(jù)索引按照塊創(chuàng)建,塊越多,索引越大。每一個 HFile 還會維護(hù)一個布隆過濾器(就像是一個很大的地圖,文件中每有一種 key,就在對應(yīng)的位置標(biāo)記,讀取時可以大致判斷要 get 的 key 是否存在 HFile 中)。KeyValue 內(nèi)容如下:

rowlength -----------→ key 的長度

row -----------------→ key 的值

columnfamilylength --→ 列族長度

columnfamily --------→ 列族

columnqualifier -----→ 列名

timestamp -----------→ 時間戳(默認(rèn)系統(tǒng)時間)

keytype -------------→ Put

由于 HFile 存儲經(jīng)過序列化,所以無法直接查看。可以通過 HBase 提供的命令來查看存儲在 HDFS 上面的 HFile 元數(shù)據(jù)內(nèi)容

bin/hbase hfile -m -f /hbase/data/命名空間/表名/regionID/列族/HFile名

5.2 讀流程

創(chuàng)建連接同寫流程。

創(chuàng)建 Table 對象發(fā)送 get 請求優(yōu)先訪問 Block Cache,查找是否之前讀取過,并且可以讀取 HFile 的索引信息和布隆過濾器不管讀緩存中是否已經(jīng)有數(shù)據(jù)了(可能已經(jīng)過期了),都需要再次讀取寫緩存和store 中的文件最終將所有讀取到的數(shù)據(jù)合并版本,按照 get 的要求返回即可

5.3 合并讀取數(shù)據(jù)優(yōu)化

每次讀取數(shù)據(jù)都需要讀取三個位置,最后進(jìn)行版本的合并。效率會非常低,所有系統(tǒng)需要對此優(yōu)化

HFile 帶有索引文件,讀取對應(yīng) RowKey 數(shù)據(jù)會比較快Block Cache 會緩存之前讀取的內(nèi)容和元數(shù)據(jù)信息,如果 HFile 沒有發(fā)生變化(記錄在 HFile 尾信息中),則不需要再次讀取使用布隆過濾器能夠快速過濾當(dāng)前 HFile 不存在需要讀取的 RowKey,從而避免讀取文件。(布隆過濾器使用 HASH 算法,不是絕對準(zhǔn)確的,出錯會造成多掃描一個文件,對讀取數(shù)據(jù)結(jié)果沒有影響)

6、StoreFile Compaction

由于 memstore 每次刷寫都會生成一個新的 HFile,文件過多讀取不方便,所以會進(jìn)行文件的合并,清理掉過期和刪除的數(shù)據(jù),會進(jìn)行 StoreFile Compaction。

Compaction 分為兩種,分別是 Minor Compaction 和** Major Compaction**。Minor Compaction會將臨近的若干個較小的 HFile 合并成一個較大的 HFile,并清理掉部分過期和刪除的數(shù)據(jù),有系統(tǒng)使用一組參數(shù)自動控制,Major Compaction 會將一個 Store 下的所有的 HFile 合并成一個大 HFile,并且會清理掉所有過期和刪除的數(shù)據(jù),由參數(shù) hbase.hregion.majorcompaction控制,默認(rèn) 7 天。

Minor Compaction 控制機(jī)制,參與到小合并的文件需要通過參數(shù)計算得到,有效的參數(shù)有 5 個

hbase.hstore.compaction.ratio(默認(rèn) 1.2F)合并文件選擇算法中使用的比率hbase.hstore.compaction.min(默認(rèn) 3) 為 Minor Compaction 的最少文件個數(shù)hbase.hstore.compaction.max(默認(rèn) 10) 為 Minor Compaction 最大文件個數(shù)hbase.hstore.compaction.min.size(默認(rèn) 128M)為單個 Hfile 文件大小最小值,小于這個數(shù)會被合并hbase.hstore.compaction.max.size(默認(rèn) Long.MAX_VALUE)為單個 Hfile 文件大小最大值,高于這個數(shù)不會被合并

小合并機(jī)制為拉取整個 store 中的所有文件,做成一個集合。之后按照從舊到新的順序遍歷。判斷條件為:

過小合并,過大不合并文件大小 / hbase.hstore.compaction.ratio < (剩余文件大小和) 則參與壓縮。所有把比值設(shè)置過大,如 10 會最終合并為 1 個特別大的文件,相反設(shè)置為 0.4,會最終產(chǎn)生 4 個 storeFile。不建議修改默認(rèn)值滿足壓縮條件的文件個數(shù)達(dá)不到個數(shù)要求(3 <= count <= 10)則不壓縮

7、Region Split

Region 切分分為兩種,創(chuàng)建表格時候的預(yù)分區(qū)即自定義分區(qū),同時系統(tǒng)默認(rèn)還會啟動一個切分規(guī)則,避免單個 Region 中的數(shù)據(jù)量太大

7.1 預(yù)分區(qū)(自定義分區(qū))

每一個 region 維護(hù)著 startRow 與 endRowKey,如果加入的數(shù)據(jù)符合某個 region 維護(hù)的rowKey 范圍,則該數(shù)據(jù)交給這個 region 維護(hù)。那么依照這個原則,我們可以將數(shù)據(jù)所要投放的分區(qū)提前大致的規(guī)劃好,以提高 HBase 性能

# 手動設(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)建對應(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 匯報 master,修改對應(yīng)的 Meta 表信息添加兩列 info:splitA 和 info:splitB 信息。之后需要操作 HDFS 上面對應(yīng)的文件,按照拆分后的 Region 范圍進(jìn)行標(biāo)記區(qū)分,實(shí)際操作為創(chuàng)建文件引用,不會挪動數(shù)據(jù)。剛完成拆分的時候,兩個 Region 都由原先的RegionServer 管理。之后匯報給 Master,由Master將修改后的信息寫入到Meta表中。等待下一次觸發(fā)負(fù)載均衡機(jī)制,才會修改Region的管理服務(wù)者,而數(shù)據(jù)要等到下一次壓縮時,才會實(shí)際進(jìn)行移動。

不管是否使用預(yù)分區(qū),系統(tǒng)都會默認(rèn)啟動一套 Region 拆分規(guī)則。不同版本的拆分規(guī)則有差別。系統(tǒng)拆分策略的父類為 RegionSplitPolicy。

0.94 版本之前 => ConstantSizeRegionSplitPolicy 當(dāng) 1 個 region 中 的 某 個 Store 下 所 有 StoreFile 的 總 大 小 超 過 hbase.hregion.max.filesize (10G),該 Region 就會進(jìn)行拆分 0.94 版本之后,2.0 版本之前 => IncreasingToUpperBoundRegionSplitPolicy 當(dāng) 1 個 region 中 的 某 個 Store 下 所 有 StoreFile 的 總 大 小 超 過Min(initialSize*R^3 ,hbase.hregion.max.filesize"),該 Region 就會進(jìn)行拆分。其中 initialSize 的默認(rèn)值為 2*hbase.hregion.memstore.flush.size,R 為當(dāng)前 Region Server 中屬于該 Table 的Region 個數(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 上該表只有一個 Region,按照 2 * hbase.hregion.memstore.flush.size 分裂,否則按照 hbase.hregion.max.filesize 分裂

五、HBase 優(yōu)化

1、RowKey 設(shè)計

一條數(shù)據(jù)的唯一標(biāo)識就是 rowkey,那么這條數(shù)據(jù)存儲于哪個分區(qū),取決于 rowkey 處于哪個一個預(yù)分區(qū)的區(qū)間內(nèi),設(shè)計 rowkey的主要目的 ,就是讓數(shù)據(jù)均勻的分布于所有的 region中,在一定程度上防止數(shù)據(jù)傾斜。接下來我們就談一談 rowkey 常用的設(shè)計方案

生成隨機(jī)數(shù)、hash、散列值時間戳反轉(zhuǎn)字符串拼接

現(xiàn)在有一個需求,源數(shù)據(jù)如下

user data pay

zhangsan 2022-01-05 09:08:00 100

統(tǒng)計張三在 2021 年 12 月份消費(fèi)的總金額統(tǒng)計所有人在 2021 年 12 月份消費(fèi)的總金額

1.1 實(shí)現(xiàn)需求 1

為了能夠統(tǒng)計張三在 2021 年 12 月份消費(fèi)的總金額,我們需要用 scan 命令能夠得到張三在這個月消費(fèi)的所有記錄,之后在進(jìn)行累加即可。Scan 需要填寫 startRow 和 stopRow:

scan : startRow -> ^A^Azhangsan2021-12

endRow -> ^A^Azhangsan2021-12.

避免掃描數(shù)據(jù)混亂,解決字段長度不一致的問題,可以使用相同阿斯卡碼值的符號進(jìn)行填充,框架底層填充使用的是阿斯卡碼值為 1 的^A最后的日期結(jié)尾處需要使用阿斯卡碼略大于’-'的值

最終得到 rowKey 的設(shè)計為

# 注意 rowkey 相同的數(shù)據(jù)會視為相同數(shù)據(jù)覆蓋掉之前的版本

rowKey: userdate(yyyy-MM-dd HH:mm:SS)

1.2 實(shí)現(xiàn)需求 2

問題提出:按照需要 1 的 rowKey 設(shè)計,會發(fā)現(xiàn)對于需求 2,完全沒有辦法寫 rowKey 的掃描范圍。此處能夠看出 hbase 設(shè)計 rowKey 使用的特點(diǎn)為:**適用性強(qiáng) 泛用性差 能夠完美實(shí)現(xiàn)一個需求 但是不能同時完美實(shí)現(xiàn)多個需要。**如果想要同時完成兩個需求,需要對 rowKey 出現(xiàn)字段的順序進(jìn)行調(diào)整。調(diào)整的原則為:可枚舉的放在前面。其中時間是可以枚舉的,用戶名稱無法枚舉,所以必須把時間放在前面。

# 最終滿足 2 個需求的設(shè)計,可以窮舉的寫在前面即可

rowKey 設(shè)計格式 => date(yyyy-MM)^A^Auserdate(-dd hh:mm:ss ms)

#(1)統(tǒng)計張三在 2021 年 12 月份消費(fèi)的總金額

scan: startRow => 2021-12^A^Azhangsan

stopRow => 2021-12^A^Azhangsan.

#(2)統(tǒng)計所有人在 2021 年 12 月份消費(fèi)的總金額

scan: startRow => 2021-12

stopRow => 2021-12.

1.3 添加預(yù)分區(qū)優(yōu)化

預(yù)分區(qū)的分區(qū)號同樣需要遵守 rowKey 的 scan 原則。所有必須添加在 rowKey 的最前面,前綴為最簡單的數(shù)字。同時使用 hash 算法將用戶名和月份拼接決定分區(qū)號。(單獨(dú)使用用戶名會造成單一用戶所有數(shù)據(jù)存儲在一個分區(qū))

# 添加預(yù)分區(qū)優(yōu)化

startKey stopKey

001

001 002

002 003

...

119 120

# 分區(qū)號=> hash(user+date(MM)) % 120

# 分區(qū)號填充 如果得到 1 => 001

rowKey 設(shè)計格式 => 分區(qū)號 date(yyyy-MM)^A^Auserdate(-dd hh:mm:ss ms)

# 缺點(diǎn):實(shí)現(xiàn)需求 2 的時候,由于每個分區(qū)都有 12 月份的數(shù)據(jù),需要掃描 120 個分區(qū)

# 解決方法:提前將分區(qū)號和月份進(jìn)行對應(yīng)

# 提前將月份和分區(qū)號對應(yīng)一下

#000 到 009 分區(qū) 存儲的都是 1 月份數(shù)據(jù)

#010 到 019 分區(qū) 存儲的都是 2 月份數(shù)據(jù)

#...

#110 到 119 分區(qū) 存儲的都是 12 月份數(shù)據(jù)

#是 9 月份的數(shù)據(jù)

分區(qū)號=> hash(user+date(MM)) % 10 + 80

分區(qū)號填充 如果得到 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 會話超時時間

進(jìn)入hbase-site.xml修改

屬性:zookeeper.session.timeout解釋:默認(rèn)值為 90000 毫秒(90s)。當(dāng)某個 RegionServer 掛掉,90s 之后 Master 才能察覺到。可適當(dāng)減小此值,盡可能快地檢測 regionserver 故障,可調(diào)整至 20-30s

# 看你能有都能忍耐超時,同時可以調(diào)整重試時間和重試次數(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ù)客戶端的請求數(shù)進(jìn)行調(diào)整,讀寫請求較多時,增加此值。

2.3 手動控制 Major Compaction

屬性:hbase.hregion.majorcompaction解釋:默認(rèn)值:604800000 秒(7 天), Major Compaction 的周期,若關(guān)閉自動 Major Compaction,可將其設(shè)為 0。如果關(guān)閉一定記得自己手動合并,因?yàn)榇蠛喜⒎浅S幸饬x

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

屬性:hbase.hregion.max.filesize解釋:默認(rèn)值 10737418240(10GB),如果需要運(yùn)行 HBase 的 MR 任務(wù),可以減小此值,因?yàn)橐粋€ region 對應(yīng)一個 map 任務(wù),如果單個 region 過大,會導(dǎo)致 map 任務(wù)執(zhí)行時間過長。該值的意思就是,如果 HFile 的大小達(dá)到這個數(shù)值,則這個 region 會被切分為兩個 Hfile

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

屬性:hbase.client.write.buffer解釋:默認(rèn)值 2097152bytes(2M)用于指定 HBase 客戶端緩存,增大該值可以減少 RPC調(diào)用次數(shù),但是會消耗更多內(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,讀請求比較多的情況下,可適當(dāng)調(diào)大

2.8 MemStore 占用 RegionServer 堆內(nèi)存的比例

屬性:hbase.regionserver.global.memstore.size解釋:默認(rèn) 0.4,寫請求較多的情況下,可適當(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 是并行垃圾回收,會有大量的暫停。理由是 HBsae 大量使用內(nèi)存用于存儲數(shù)據(jù),容易遭遇數(shù)據(jù)洪峰造成 OOM,同時寫緩存的數(shù)據(jù)是不能垃圾回收的,主要回收的就是讀緩存,而讀緩存垃圾回收不影響性能,所以最終設(shè)置的效果可以總結(jié)為:防患于未然,早洗早輕松

# 設(shè)置使用 CMS 收集器(并發(fā))

-XX:+UseConcMarkSweepGC

# 保持新生代盡量小,同時盡早開啟 GC,例如

# 在內(nèi)存占用到 70%的時候開啟 GC

-XX:CMSInitiatingOccupancyFraction=70

# 指定使用 70%,不讓 JVM 動態(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è)置多個與 max.result.size * handler.count 相乘的結(jié)果小于 Survivor Space(新生代經(jīng)過垃圾回收之后存活的對象)

4、HBase 使用經(jīng)驗(yàn)法則

Region 大小控制 10-50Gcell 大小不超過 10M(性能對應(yīng)小于 100K 的值有優(yōu)化),如果使用 mob(Medium-sized Objects 一種特殊用法)則不超過 50M1 張表有 1 到 3 個列族,不要設(shè)計太多。最好就 1 個,如果使用多個盡量保證不會同時讀取多個列族1 到 2 個列族的表格,設(shè)計 50-100 個 Region列族名稱要盡量短,不要去模仿 RDBMS(關(guān)系型數(shù)據(jù)庫)具有準(zhǔn)確的名稱和描述如果 RowKey 設(shè)計時間在最前面,會導(dǎo)致有大量的舊數(shù)據(jù)存儲在不活躍的 Region中,使用的時候,僅僅會操作少數(shù)的活動 Region,此時建議增加更多的 Region 個數(shù)如果只有一個列族用于寫入數(shù)據(jù),分配內(nèi)存資源的時候可以做出調(diào)整,即寫緩存不會占用太多的內(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 之間放一個 Phoenix 中間層不會減慢速度,因?yàn)橛脩艟帉懙臄?shù)據(jù)處理代碼和 Phoenix 編寫的沒有區(qū)別(更不用說你寫的垃圾的多),不僅如此Phoenix 對于用戶輸入的 SQL 同樣會有大量的優(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 包并拷貝到各個節(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

# 錯誤解決

# 出現(xiàn)下面錯誤的原因是之前使用過 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)建表

# 直接指定單個列作為 RowKey

CREATE TABLE IF NOT EXISTS student(

id VARCHAR primary key,

name VARCHAR,

age BIGINT,

addr VARCHAR);

# 在 phoenix 中,表名等會自動轉(zhuǎn)換為大寫,若要小寫,使用雙引號,如"us_population"。

# 指定多個列的聯(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 中建表,會在 HBase 中創(chuàng)建一張對應(yīng)的表。為了減少數(shù)據(jù)對磁盤空間的占,Phoenix 默認(rèn)會對 HBase 中的列名做編碼處理。

# 具體規(guī)則可參考官網(wǎng)鏈接:https://phoenix.apache.org/columnencoding.html,若不想對列名編碼,可在建表語句末尾加上 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)行表的映射。映射方式有兩種:視圖映射和表映射

# 啟動

/opt/module/hbase/bin/hbase shell

# 創(chuàng)建 HBase 表 test

create 'test','info1','info2'

# 視圖映射

# Phoenix 創(chuàng)建的視圖是只讀的,所以只能用來做查詢,無法通過視圖對數(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 中被映射的表也會被刪除

# 注:進(jìn)行表映射時,不能使用列名編碼,需將 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ù)字,底層存儲為補(bǔ)碼,而 Phoenix 中的數(shù)字,底層存儲為在補(bǔ)碼的基礎(chǔ)上,將符號位反轉(zhuǎn)。故當(dāng)在 Phoenix 中建表去映射 HBase 中已存在的表,當(dāng) HBase 中有數(shù)字類型的字段時,會出現(xiàn)解析錯誤的現(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 等無符號類型,其對數(shù)字的編碼解碼方式和 HBase 是相同的,如果無需考慮負(fù)數(shù),那在 Phoenix 中建表時采用無符號類型是最合適的選擇

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ù)字類型的最高位,即符號位反轉(zhuǎn)即可,自定義函數(shù)可參考如下鏈接:https://phoenix.apache.org/udf.html

2.3 Phoenix JDBC 操作

此處演示一個標(biāo)準(zhǔn)的 JDBC 連接操作,實(shí)際開發(fā)中會直接使用別的框架內(nèi)嵌的 Phoenix 連接,胖客戶端加入maven依賴

org.apache.phoenix

phoenix-client-hbase-2.4

5.1.2

編寫代碼

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 沒有賬號密碼

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)部需要獲取一個 HBase 連接,所以會延遲關(guān)閉

// 不影響后續(xù)的代碼執(zhí)行

System.out.println("hello");

}

}

3、Phoenix 二級索引

3.1 二級索引配置文件

添加如下配置到 HBase 的 HRegionserver 節(jié)點(diǎn)的 hbase-site.xml

hbase.regionserver.wal.codec

org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec

3.2 全局索引(global index)

Global Index 是默認(rèn)的索引格式,創(chuàng)建全局索引時,會在 HBase 中建立一張新表。也就是說索引數(shù)據(jù)和數(shù)據(jù)表是存放在不同的表中的,因此全局索引適用于多讀少寫的業(yè)務(wù)場景。

寫數(shù)據(jù)的時候會消耗大量開銷,因?yàn)樗饕硪惨?,而索引表是分布在不同的?shù)據(jù)節(jié)點(diǎn)上的,跨節(jié)點(diǎn)的數(shù)據(jù)傳輸帶來了較大的性能消耗。在讀數(shù)據(jù)的時候 Phoenix 會選擇索引表來降低查詢消耗的時間

# 創(chuàng)建單個字段的全局索引

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;

# 查看二級索引是否有效,可以使用 explainPlan 執(zhí)行計劃,有二級索引之后會變成范圍掃描

explain select id,name from student1 where age = 10;

# 如果想查詢的字段不是索引字段的話索引表不會被使用,也就是說不會帶來查詢速度的提升

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í)行計劃查看效果,使用explain

3.4 本地索引(local index)

Local Index 適用于寫操作頻繁的場景。索引數(shù)據(jù)和數(shù)據(jù)表的數(shù)據(jù)是存放在同一張表中(且是同一個 Region),避免了在寫操作的時候往不同服務(wù)器的索引表中寫索引帶來的額外開銷

# my_column 可以是多個

CREATE LOCAL INDEX my_index ON my_table (my_column);

# 本地索引會將所有的信息存在一個影子列族中,雖然讀取的時候也是范圍掃描,但是沒有全局索引快,優(yōu)點(diǎn)在于不用寫多個表了

#刪除之前的索引

drop index my_index on student1;

#創(chuàng)建本地索引

CREATE LOCAL INDEX my_index ON student1 (age,addr);

#使用執(zhí)行計劃

explain select id,name,addr from student1 where age = 10;

七、Hive集成

1、集成使用

1.1 使用場景

如果大量的數(shù)據(jù)已經(jīng)存放在 HBase 上面,需要對已經(jīng)存在的數(shù)據(jù)進(jìn)行數(shù)據(jù)分析處理,那么 Phoenix 并不適合做特別復(fù)雜的 SQL 處理,此時可以使用 hive 映射 HBase 的表格,之后寫 HQL 進(jìn)行分析處理。

1.2 配置

在 hive-site.xml 中添加 zookeeper 的屬性

hive.zookeeper.quorum

hadoop102,hadoop103,hadoop104

hive.zookeeper.client.port

2181

2、案例舉例

2.1 案例一

目標(biāo):建立 Hive 表,關(guān)聯(lián) HBase 表,插入數(shù)據(jù)到 Hive 表的同時能夠影響 HBase 表。

# 在 Hive 中創(chuàng)建表同時關(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 查看,都生成了對應(yīng)的表

# (2)在 Hive 中創(chuàng)建臨時中間表,用于 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)存儲了某一張表 hbase_emp_table,然后在 Hive 中創(chuàng)建一個外部表來關(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;

柚子快報邀請碼778899分享:HBase2.x學(xué)習(xí)筆記

http://yzkb.51969.com/

參考鏈接

評論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。

轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://m.gantiao.com.cn/post/18785922.html

發(fā)布評論

您暫未設(shè)置收款碼

請在主題配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問

文章目錄