一、HBase簡介
1.1 定義
-- 1. HBase是什么糖荒?
1. 分布式
2. 可擴(kuò)展
3. 支持海量數(shù)據(jù)的存儲
4. NoSQL的數(shù)據(jù)庫靖苇。
-- 2. 說明:
a席噩、NoSQL: Not only SQL,不僅僅是一個數(shù)據(jù)庫
b贤壁、是基于谷歌的三篇論文之bigtable生成的悼枢。
c、HBase:理解為Hadoop base
-- 3. 大數(shù)據(jù)框架:
a脾拆、數(shù)據(jù)的存儲:hdfs / hive / hbase
b馒索、數(shù)據(jù)的傳輸:flume / sqoop
c莹妒、數(shù)據(jù)的計(jì)算:tez / mr / spark / flink
-- 4. 和傳統(tǒng)數(shù)據(jù)庫的差別:
傳統(tǒng)數(shù)據(jù)庫的結(jié)構(gòu):數(shù)據(jù)庫 --> 表 --> 行和列
HBase的結(jié)構(gòu) : namespace(命名空間) --> table --> 列族 --> 行和列 --> orgion --> store
HBase可以理解為多維的map,嵌套的map結(jié)構(gòu)绰上。
1.2 HBase數(shù)據(jù)模型
1.2.1 HBase邏輯結(jié)構(gòu)
1.2.2 HBase的物理結(jié)構(gòu)
1.2.3 數(shù)據(jù)模型
--1. HBase表的幾個概念
1. 'namespace':命名空間旨怠,類似mysql的數(shù)據(jù)庫,在HBase中默認(rèn)有兩個namespace:default/HBase
2. '列族' :
3. 'column' :列蜈块,在使用時鉴腻,格式為:'列族名:列名'
4. 'row' :行,在HBase中百揭,行是邏輯概念上的爽哎,在物理內(nèi)存中,同一行的數(shù)據(jù)很可能不在一起的器一。
"那么我們通過什么參數(shù)來判斷兩個數(shù)據(jù)是不是屬于同一行呢"课锌?
就是下面的rowkey,rowkey相同祈秕,就表示是同一行的數(shù)據(jù)渺贤。
5. 'rowKey' :行的標(biāo)簽,唯一定位行的標(biāo)識
6. 'region' :區(qū)域请毛,表示多行數(shù)據(jù)志鞍,在HBase中,一個table默認(rèn)是一個region
7. 'store' :在同一個region中获印,列族的個數(shù) = store的個數(shù),store有兩種:memstore/storefile
8. 'timeStemp':時間戳述雾,表示數(shù)據(jù)執(zhí)行的時間,
每執(zhí)行一次操作就會生成一個版本兼丰。
9. 'table' :表玻孟,可以理解為多維的map,在創(chuàng)建表的時候鳍征,
只需要聲明表名和列族就可以.
"非常適用于非結(jié)構(gòu)化數(shù)據(jù)黍翎,不需要指定數(shù)據(jù)的格式"
10. 'cell' : 單元格,在表中的同一個位置'某一行的某一列位置'艳丛,會有多個cell匣掸,相同的位置每修改一次,就會生成一個cell氮双。
由{rowkey,列族:列名碰酝,time stamp},進(jìn)行唯一標(biāo)識戴差。
-- 2. HBase集群的幾個概念
1. 'master':可以理解為hadoop的NM送爸,專門負(fù)責(zé)管理小弟'regionServer',實(shí)現(xiàn)類為:Hmaster
2. 'regionServer':可以理解為hadoop的DM,專門負(fù)責(zé)管理region中的數(shù)據(jù)袭厂,還有兩個組件:WAL'可理解為NM的內(nèi)存區(qū)域的元數(shù)
據(jù)'和balckcache墨吓,至于具體是干嘛的,后面寫操作的時候講纹磺,實(shí)現(xiàn)類為:HRegionServer
3. 'meta' : 元數(shù)據(jù)帖烘,zookeeper有它的地址,某一個regionServer保存著這個數(shù)據(jù)橄杨。主要有table和region所在的位置秘症。
4. 'zookeeper':① master的高可用 ② RegionServer的監(jiān)控 ③ Region的元數(shù)據(jù)管理。
-- 3. 說明:
在底層存儲時讥珍,數(shù)據(jù)按照rowkey的字典順序從小到大進(jìn)行排列历极。
二、 HBase的安裝
2.1 部署zookeeper和Hadoop
2.2 解壓HBase到指定的位置
[atguigu@hadoop105 software]$ tar -zxvf hbase-2.0.5-bin.tar.gz -C /opt/module
2.3 配置環(huán)境變量
[atguigu@hadoop105 ~]$ sudo vim /etc/profile.d/my_env.sh
- 添加
#HBASE_HOME
export HBASE_HOME=/opt/module/hbase-2.0.5
export PATH=$PATH:$HBASE_HOME/bin
2.4 配置HBase的文件
- 修改HBase對應(yīng)的配置文件
#export HBASE_MANAGES_ZK=true
修改至:
export HBASE_MANAGES_ZK=false
- hbase-site.xml增加如下配置內(nèi)容:
<configuration>
<property>
<name>hbase.rootdir</name>
<value>hdfs://hadoop105:8020/hbase</value>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
<property>
<name>hbase.zookeeper.quorum</name>
<value>hadoop105,hadoop106,hadoop107</value>
</property>
<property>
<name>hbase.unsafe.stream.capability.enforce</name>
<value>false</value>
</property>
<property>
<name>hbase.wal.provider</name>
<value>filesystem</value>
</property>
</configuration>
- 配置regionservers
hadoop105
hadoop106
hadoop107
2.5 分發(fā)HBase
[atguigu@hadoop105 module]$ xsync hbase-2.0.5
2.6 啟動HBase
先啟動hadoop集群衷佃,再啟動zk,然后啟動HBase
- 單點(diǎn)啟動
hbase-daemon.sh start master
hbase-daemon.sh start regionserver
-daemon是指后臺啟動蹄葱。
- 群起
[atguigu@hadoop105 hbase-2.0.5]$ start-hbase.sh
-- 說明:
a氏义、會啟動當(dāng)前節(jié)點(diǎn)的master
b、在所有節(jié)點(diǎn)啟動regionServer
c图云、Hmaster和regionServer都是一個進(jìn)程惯悠。
2.7 查看HBase的頁面
網(wǎng)址:hadoop105:16010
2.8 master的高可用(可選)
- 關(guān)閉HBase的集群
stop-hbase.sh
- 在conf目錄下創(chuàng)建backup-masters文件
touch conf/backup-masters
- 在backup-masters文件中配置高可用HMaster節(jié)點(diǎn)
echo hadoop106 > conf/backup-masters
- 將整個conf目錄scp到其他節(jié)點(diǎn)
xsync conf
三、 HBase的操作
3.1 基本操作
- 進(jìn)入hbase的客戶端
[atguigu@hadoop105 conf]$ hbase shell
- 查看所有namespace中所有的表
hbase(main):001:0> list
TABLE
test:user #test是一個namespace
user #沒有寫命名空間的竣况,則默認(rèn)是default
2 row(s)
Took 0.4410 seconds
=> ["test:user", "user"]
3.2 表的操作
- 創(chuàng)建表
--1.語法: create 'namespace:tablename','列族1'克婶,'列族2',...
如果是namespace是default丹泉,則可以省略
--2.實(shí)例1:hbase(main):002:0> create 'test','info','info1'
實(shí)例2:hbase(main):004:0> create 'test:lianzp','info'
- 添加數(shù)據(jù)
-- 1. 語法: put 'tablename','rowkey','列族1:列名'情萤,'value'
說明:shell操作每次只能添加一個值
-- 2. 實(shí)例: hbase(main):008:0> put 'user','1001','info1:age',20
- 全局掃描數(shù)據(jù)
-- 1. 語法: scan 'tablename'
-- 2. 實(shí)例: hbase(main):009:0> scan 'user'
-- 3. 打印結(jié)果:#如下是顯示了4行數(shù)據(jù),實(shí)際上數(shù)據(jù)在表中屬于3行
ROW COLUMN+CELL
1000 column=info2:sex, timestamp=1592894945017, value=woman
1001 column=info1:age, timestamp=1592911858092, value=20
1001 column=info1:name, timestamp=1592894905511, value=lianzp
1003 column=info1:age, timestamp=1592894925226, value=50
- 掃描指定行的數(shù)據(jù)
-- 1. 語法:scan 'tablename',{STARTROW=>'rowkey',STOPROW=>'rowkey'}
-- 2. 說明:
a摹恨、區(qū)間為左閉右開
b筋岛、如果STARTROW沒有寫,則表示開始的rowkey為負(fù)無窮晒哄,即沒有下限睁宰,只有上限
如果STOPROW沒有寫,則表示結(jié)束的rowkey為正無窮寝凌,即沒有上限柒傻,只有下限
-- 3. 實(shí)例:hbase(main):012:0> scan 'user',{STARTROW=>'1000',STOPROW=>'1003'}
hbase(main):014:0> scan 'user',{STARTROW=>'1000'}
hbase(main):015:0> scan 'user',{STOPROW=>'1001'}
-- 4. 結(jié)果:
ROW COLUMN+CELL
1000 column=info2:sex, timestamp=1592894945017, value=woman
1001 column=info1:age, timestamp=1592911858092, value=20
1001 column=info1:name, timestamp=1592894905511, value=lianzp
2 row(s)
- 獲取某一行的數(shù)據(jù)
-- 1. 語法: get 'tablename','rowkey'
-- 2. 實(shí)例: get 'user','1001'
- 獲取某一行指定列的數(shù)據(jù)
-- 1. 語法: get 'tablename','rowkey','列族1:列名'
-- 2. 實(shí)例: get 'user','1001','info1:age'
- 獲取行的數(shù)量
-- 1. 語法: count 'tablename'
-- 2. 實(shí)例: hbase(main):001:0> count 'user'
- 查詢表的結(jié)構(gòu)
-- 1. 語法: describe 'tablename'
-- 2. 實(shí)例: hbase(main):002:0> describe 'user'
- 刪除某一行某一列的值
-- 1. 語法: delete 'tablename','rowkey'较木,'列族1:列名'
-- 2. 實(shí)例: hbase(main):002:0> hbase(main):005:0> delete 'user','1001','info1:age'
-- 3. 說明:
a红符、一個位置默認(rèn)保留一個版本,如果一個位置被多次修改時,刪除當(dāng)前的數(shù)據(jù)违孝,再進(jìn)行查詢時刹前,在未flush的情況下,上一個版本的數(shù)據(jù)
可以被查詢出來雌桑。
b喇喉、此處的刪除并不是真正的刪除,只是給這個刪除的數(shù)據(jù)打上了一個標(biāo)記校坑,只有當(dāng)落盤flush的時候拣技,才會真正的被清理掉。
10 . 刪除某一行的全部數(shù)據(jù)
-- 1. 語法: deleteall 'tablename','rowkey'
-- 2. 實(shí)例: hbase(main):020:0> deleteall 'user','1001'
- 清除表數(shù)據(jù)
-- 1. 語法: truncate 'tablename'
-- 2. 實(shí)例:hbase(main):020:0> truncate 'user'
-- 3. 說明:
a耍目、在執(zhí)行的過程中膏斤,首先會自動disable 'user'
b、然后再清空表數(shù)據(jù),truncate 'user'
-- 4. 打印結(jié)果
Truncating 'user' table (it may take a while):
Disabling table...
Truncating table...
Took 1.5164 seconds
- 刪除表
-- 1. 語法: drop 'tablename'
-- 2. 實(shí)例: hbase(main):020:0> drop 'user'
-- 3. 說明:
如果直接drop表邪驮,會報(bào)錯:ERROR: Table student is enabled. Disable it first
a莫辨、在刪除表時,需要手動將表設(shè)置為:disable 'tablename'
b毅访、然后再刪除,drop 'tablename'
- 多版本
-- 1.設(shè)置多版本
alter 'student' ,{NAME => 'info',VERSIONS => 3}
-- 2.查詢多版本
get 'student','1001',{COLUMN => 'info:age' ,VERSIONS => 3 }
3.3 namespace操作
- 創(chuàng)建namespace
hbase(main):037:0> create_namespace 'lianzp'
- 查詢namespace
hbase(main):039:0> list_namespace
- 創(chuàng)建自定的namespace的表
hbase(main):042:0> create 'lianzp:test','info'
- 查詢指定namespace的所有表
hbase(main):044:0> list_namespace_tables 'lianzp'
- 刪除namespace
hbase(main):049:0> drop_namespace 'lianzp'
-- 說明:在刪除namespace之前沮榜,需要先刪除namespace的表,刪除表時喻粹,需要先將表置于不可用的狀態(tài)蟆融。
四、HBase進(jìn)階
4.1 Region Server的架構(gòu)
image.png
--1. 說明:
a守呜、1個Region Server = 1個WAL + 1個 Block Cache + N個regoin
b型酥、1個Region = 1個MemStore + N個StoreFile
-- 2. Region Server
a、是一個進(jìn)程查乒,是HBase分布式的一個節(jié)點(diǎn)弥喉。
-- 3. WAL
a、預(yù)防日志侣颂,存儲在hdfs上档桃,向RegionServer發(fā)送請求時,首先會經(jīng)過WAL憔晒,將具體的操作保存下來藻肄,然后再進(jìn)行具體的操作;
b拒担、作用:當(dāng)regionServer出現(xiàn)故障時嘹屯,因?yàn)閃AL保留了具體的操作,所以數(shù)據(jù)不會丟失从撼。
-- 4. Block Cache
a州弟、讀緩存钧栖,每次查詢出的數(shù)據(jù)會緩存在BlockCache中较性,方便下次查詢
-- 5. Region
a甚淡、是一張表中的多行數(shù)據(jù)却汉,默認(rèn)是一個Region妓雾。
-- 6. Store
a、對應(yīng)一張表中的列族的個數(shù)梢褐。
b套么、有兩種蟹漓,分別是MemStore 和StoreFile
-- 7. memstore
a最蕾、數(shù)據(jù)是先存儲在MemStore中依溯,排好序后,等到達(dá)刷寫時機(jī)才會刷寫到HFile瘟则,每次刷寫都會形成一個新的HFile
-- 8. StoreFile
a黎炉、StoreFile以Hfile的形式存儲在HDFS上
b、數(shù)據(jù)在每個StoreFile中都是有序的
4.2 寫流程
image.png
--1. 具體流程:
1. Client先訪問zookeeper醋拧,請求hbase:meta元數(shù)據(jù)位于哪個Region Server中慷嗜。
2. zookeeper返回hbase:meta所在的Region Server地址
3. client訪問對應(yīng)的Region Server,請求hbase:meta元數(shù)據(jù)信息
4. Region Server返回meta元數(shù)據(jù)信息
5. client根據(jù)寫請求的namespace:table/rowkey趁仙,查詢出目標(biāo)數(shù)據(jù)位于哪個Region Server中的哪個Region中洪添。
并將該table的region信息以及meta表的位置信息緩存在客戶端的meta cache,方便下次訪問雀费。
6. 與目標(biāo)Region Server進(jìn)行通訊;
7. 將數(shù)據(jù)順序?qū)懭耄ㄗ芳樱┑絎AL痊焊;
8. WAL將數(shù)據(jù)寫入對應(yīng)的MemStore盏袄,數(shù)據(jù)會在MemStore進(jìn)行排序;
9. 完成寫數(shù)據(jù)操作以后薄啥,向客戶端發(fā)送ack辕羽;
10. 等達(dá)到MemStore的刷寫時機(jī)后,將數(shù)據(jù)刷寫到HFile垄惧。
-- 2. hbase:meta包含哪些信息呢刁愿?
命令:scan ‘hbase:meta’
a、rowkey: test,,1592911245995.c42a3b247c7ed78f071
1) test:namespace,命名空間
2) ,,:startkey endkey到逊,起止的rowkey
3) 1592911245995:time stamp 铣口,時間戳
4) .c42a3b247c7ed78f071:前面3個參數(shù)的MD5值。
b觉壶、column=info:regioninfo : region的信息脑题,
value={ENCODED => c42a3b247c7ed78f071f60721bad78ad, NAME => 'test,,
f60721bad78ad.1592911245995.c42a3b247c7ed78f071f60721bad78ad.', STARTKEY => '', ENDKEY => ''}
c、column=info:seqnumDuringOpen :序列號铜靶,value=\x00\x00\x00\x00\x00\x00\x00\x02
d叔遂、column=info:server : 該region所在的server,value=hadoop106:16020
e、column=info:serverstartcode : 該region所在的server創(chuàng)建的時間戳已艰,value=1592887276902
f痊末、column=info:sn : value=hadoop106,16020,1592887276902
g、column=info:state :該region的狀態(tài)哩掺,value=OPEN
4.3 MemStore flush時機(jī)
-- MemStore刷寫時機(jī):
如下一共有4種刷寫實(shí)時機(jī)凿叠,任何一個時機(jī)點(diǎn)到達(dá)后均會進(jìn)行刷寫,刷寫分為:
a疮丛、刷寫:memStore向storeFile中寫數(shù)據(jù)幔嫂。
b、阻止寫:阻止客戶端往region中寫數(shù)據(jù)誊薄。
-- 時機(jī)1:region級別履恩,刷寫方式:當(dāng)前region中所有memstore都刷寫
a、'刷寫':當(dāng)某個memstroe的大小達(dá)到了'hbase.hregion.memstore.flush.size'(默認(rèn)值128M)呢蔫,
時切心,該region中所有memstore都會刷寫。
'每一個region有多個store片吊,一個store包含了一個memStore和多個storeFile'
b绽昏、'阻止寫':當(dāng)memstore的大小達(dá)到了'hbase.hregion.memstore.flush.size(默認(rèn)值128M)
* hbase.hregion.memstore.block.multiplier(默認(rèn)值4)'
時512MB,會阻止繼續(xù)往該memstore寫數(shù)據(jù)俏脊。
-- 時機(jī)2: regionServer級別全谤,刷寫方式:按照所有memstore的數(shù)據(jù)大小順序(由大到小)依次進(jìn)行刷寫
a爷贫、'刷寫':當(dāng)regionserver中memstore的總大小達(dá)到
'java_heapsize*hbase.regionserver.global.memstore.size(默認(rèn)值0.4)
*hbase.regionserver.global.memstore.size.lower.limit(默認(rèn)值0.95)'认然,
即 0.38 * JVM堆內(nèi)存
region會按照其所有memstore的大小順序(由大到小)依次進(jìn)行刷寫漫萄。
直到region server中所有memstore的總大小減小到上述值以下卷员。
b、'阻止寫':當(dāng)region server中memstore的總大小達(dá)到'java_heapsize*hbase.regionserver.global.memstore.size'
(默認(rèn)值0.4)時腾务,會阻止繼續(xù)往所有的memstore寫數(shù)據(jù)毕骡。
-- 時機(jī)3:時間級別:刷寫方式:默認(rèn)1H進(jìn)行刷寫
a、'刷寫':到達(dá)自動刷寫的時間岩瘦,也會觸發(fā)memstore flush未巫。自動刷新的時間間隔由該屬性進(jìn)行配置
'hbase.regionserver.optionalcacheflushinterval(默認(rèn)1小時)。'
-- 時機(jī)4:關(guān)閉hbase時担钮,刷寫方式:進(jìn)行刷寫
a橱赠、'刷寫':關(guān)閉hbase即進(jìn)行刷寫。
4.4 讀流程
image.png
image.png
-- 整體的流程和寫的流程大致一致箫津,主要的區(qū)別在于:
1. 讀的位置有很多位置
a狭姨、block cache
b宰啦、memStore
c、StoreFile
2. 不同的位置饼拍,均會有數(shù)據(jù)赡模,但是數(shù)據(jù)的版本可能不一樣,所以當(dāng)客戶端讀取多個版本數(shù)據(jù)時师抄,可能每個位置都需要讀取漓柑。
3. 讀取到每個位置的數(shù)據(jù)以后,然后進(jìn)行merge叨吮,將數(shù)據(jù)進(jìn)行合并辆布,最后發(fā)送給客戶端。
4. 客戶端每次讀取到的數(shù)據(jù)最后會緩存到block cache中茶鉴,緩沖內(nèi)存的大小'默認(rèn)大小為64kb'
4.5 StoreFile Compaction
image.png
--1. StoreFile文件合并的原因
1. memstore每次刷寫都會生成一個新的HFile锋玲。--小文件
2. 同一個字段的不同版本(timestamp)和不同類型(Put/Delete)有可能會分布在不同的HFile中
3. 為了減少HFile的個數(shù)
4. 清理掉過期和刪除的數(shù)據(jù)
--2. 說明:
a、什么是過期的數(shù)據(jù)涵叮?
table中每一個位置有保留的版本數(shù)惭蹂,同一位置進(jìn)行多次數(shù)據(jù)修改時,就會有多個版本割粮,在flush之前盾碗,數(shù)據(jù)不會丟失,在落盤時舀瓢,只會
保留對應(yīng)版本數(shù)量的最新的數(shù)據(jù)廷雅,超過版本數(shù)量的數(shù)據(jù)就屬于過期數(shù)據(jù)。
b京髓、什么是刪除的數(shù)據(jù)榜轿?
使用delete刪除數(shù)據(jù)時,數(shù)據(jù)并不是真正的刪除朵锣,而是給數(shù)據(jù)打一個標(biāo)識,當(dāng)落盤時甸私,這些數(shù)據(jù)就會被過濾和刪除诚些。
--3. 合并分類:
a、Minor Compaction皇型,小合并诬烹,臨近的若干個較小的HFile合并成一個較大的HFile,并清理掉部分過期和刪除的數(shù)據(jù)弃鸦。默認(rèn)是3個文件
b绞吁、Major Compaction,大合并唬格,將一個Store下的所有的HFile合并成一個大HFile家破,并且會清理掉所有過期和刪除的數(shù)據(jù)颜说。
4.6 多版本
#創(chuàng)建一個列族的版本數(shù)為2
create 't1' ,{NAME=>'f1,VERSIONS=>2}
#獲取兩個2個版本
get 't1','1002',{CLOUMN => 'f1:age',VERSIONS => 2}
4.7 Region Split
--1. Region基本介紹
1. 默認(rèn)一個Table汰聋,只有一個Region
2. 隨著數(shù)據(jù)的不斷寫入门粪,Region會自動進(jìn)行拆分,'如果設(shè)定了region分區(qū)的規(guī)則烹困,那么這個默認(rèn)切分規(guī)則不會有效'
3. 拆分以后玄妈,HMaster有可能會將某個Region轉(zhuǎn)移到其他的Region Server,涉及到數(shù)據(jù)的遷移髓梅,IO
--2. Region切分的時機(jī)
'情況1':0.94版本之前
當(dāng)1個region中的某個Store下所有StoreFile的總大小超過hbase.hregion.max.filesize拟蜻,該Region就會進(jìn)行拆分。
'情況2':0.94版本之后
2.當(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ù)
-- 10G以前屈张,按照取最小值進(jìn)行切分,10G以后袱巨,就每10G切分一次阁谆。
具體的切分策略為:
第一次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了愉老。
'情況3':Hbase 2.0
如果當(dāng)前RegionServer上該表只有一個Region场绿,按照2 * hbase.hregion.memstore.flush.size('默認(rèn)是128M')分裂,否則按照
hbase.hregion.max.filesize('默認(rèn)是10G')分裂
4.8 一些命令
--1. 刷寫: flush 'tableName'
--2. 防止退出hbase客戶端:q
--3. 大合并:major_compact 'tableName'
--4. 小合并:compact 'tableName'
--5. scan查詢的時候:scan 'student',{COLUMNS => 'info:age:toLong'}
ROW COLUMN+CELL 1002 column=info:age, timestamp=1593192755078, value=10
五 嫉入、 HBase API
--1.DML(Data Manipulation Language)數(shù)據(jù)操縱語言:使用conn進(jìn)行操作
適用范圍:對數(shù)據(jù)庫中的數(shù)據(jù)進(jìn)行一些簡單操作焰盗,如insert,delete,update,select等.
--2.DDL(Data Definition Language)數(shù)據(jù)定義語言:使用admin對象進(jìn)行操作
適用范圍:對數(shù)據(jù)庫中的某些對象(例如,database,table)進(jìn)行管理咒林,如Create,Alter和Drop.
5.1 環(huán)境準(zhǔn)備
- 添加依賴
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>2.0.5</version>
</dependency>
5.2 DDL
package com.atguigui.lianzp
import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.hbase.{HBaseConfiguration, NamespaceDescriptor, TableName}
import org.apache.hadoop.hbase.client._
import org.apache.hadoop.hbase.util.Bytes
/**
* @Description
* *
* @author lianzhipeng
* @create 2020-06-24 9:59:55
*/
object HBase_DDL {
// 1. 獲取zookeeper的連接
val conf: Configuration = HBaseConfiguration.create()
conf.set("hbase.zookeeper.quorum", "hadoop105,hadoop106,hadoop107")
val conn: Connection = ConnectionFactory.createConnection(conf)
// 2. 獲取hbase的對象
val admin: Admin = conn.getAdmin
def main(args: Array[String]): Unit = {
val result: Boolean = tableExists("user")
// println(result)
// createTable("emp","cf1","cf2")
// deleteTable("user")
// val bool: Boolean = nsExists("lianzp")
// println(bool)
createNS("lianzp")
connclose()
}
def createNS (namespace:String) ={
if (!nsExists(namespace)){
val ns: NamespaceDescriptor.Builder = NamespaceDescriptor.create(namespace)
admin.createNamespace(ns.build())
println(s"namespace:{$namespace}創(chuàng)建成功")
}else {
println(s"namespace:{$namespace}已存在熬拒,創(chuàng)建失敗")
}
}
def nsExists(namespace : String) ={
// 獲取當(dāng)前所有的namespace的描述,是一個數(shù)組
val nss: Array[NamespaceDescriptor] = admin.listNamespaceDescriptors()
// 進(jìn)行格式轉(zhuǎn)換垫竞,獲取namespace的名字澎粟,然后確定namespace是否存在
nss.map(_.getName).contains(namespace)
}
def deleteTable (table : String) ={
// 獲取當(dāng)前表
val tn: TableName = TableName.valueOf(table)
if (tableExists(table)){
// 刪除表,首先需要將表置于不可用的狀態(tài)才能進(jìn)行刪除
admin.disableTable(tn)
admin.deleteTable(tn)
println(s"{$table}表刪除成功")
}else {
println(s"{$table}表不存在欢瞪,刪除失敗")
}
}
/**
* 判斷表是否存在
*
* @param name
* @return
*/
def tableExists(name: String) = {
val tableName: TableName = TableName.valueOf(name)
val bool: Boolean = admin.tableExists(tableName)
bool
}
/**
* 創(chuàng)建表
* @param tableName
* @param columns
*/
def createTable (tableName :String , columns: String*) ={
//TableDescriptor desc
val tn: TableName = TableName.valueOf(tableName)
val tb: TableDescriptorBuilder = TableDescriptorBuilder.newBuilder(tn)
if (!tableExists(tableName)) {
columns.foreach(cf => {
val descriptor: ColumnFamilyDescriptor = ColumnFamilyDescriptorBuilder
.newBuilder(Bytes.toBytes(cf))
.build()
tb.setColumnFamily(descriptor)
})
admin.createTable(tb.build())
println(s"{$tableName}表創(chuàng)建成功")
}else {
println(s"當(dāng)前的表{$tableName}已存在")
}
}
def connclose() = {
// 4. 關(guān)閉對象
admin.close()
// 5. 關(guān)閉連接
conn.close()
}
}
5.3 DML
package com.atguigui.lianzp
import java.util
import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.hbase.{Cell, CellUtil, HBaseConfiguration, TableName}
import org.apache.hadoop.hbase.client._
import org.apache.hadoop.hbase.util.Bytes
/**
* @Description
* *
* @author lianzhipeng
* @create 2020-06-24 14:16:48
*/
object HBase_DML {
// 1. 獲取連接
private var conf: Configuration = HBaseConfiguration.create()
conf.set("hbase.zookeeper.quorum", "hadoop105,hadoop106,hadoop107")
private val conn: Connection = ConnectionFactory.createConnection(conf)
// 2. 創(chuàng)建hbase的master的對象
private val admin: Admin = conn.getAdmin
def main(args: Array[String]): Unit = {
// 1. 增加數(shù)據(jù)
// putData("test", "1002", "info", "age", "2")
// 2. 掃描數(shù)據(jù)
// scanData("test")
// 3. 刪除數(shù)據(jù)
deleteData("test","1001","info","name")
// 4. 獲取數(shù)據(jù)
// getData("test", "1002", "info", "age")
closeConn
}
/**
* 刪除數(shù)據(jù)
*
* @param tableName
* @param rowKey
* @param columnFamily
* @param column
*/
def deleteData(tableName: String, rowKey: String, columnFamily: String, column: String) = {
// 1. 獲取表
// 封裝表
val tn: TableName = TableName.valueOf(tableName)
// 獲取表
val table: Table = conn.getTable(tn)
// 2. 判斷表是否存在
if (tableExists(tableName)) {
// 封裝一個delete
val delete = new Delete(Bytes.toBytes(rowKey))
val dl: Delete = delete.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(column))
// 對表進(jìn)行刪除
table.delete(dl)
}
}
def scanData(tableName: String) = {
// 1. 獲取表
val tn: TableName = TableName.valueOf(tableName)
val table: Table = conn.getTable(tn)
// 2.判斷表是否存在
if (tableExists(tableName)) {
// 獲取一個scan對象
val scan = new Scan()
// 獲取表的全局掃描
val scanner: ResultScanner = table.getScanner(scan)
// 這個是用來在java的集合和scala的集合之間互轉(zhuǎn) (隱式轉(zhuǎn)換)
import scala.collection.JavaConversions._
// 使用迭代器活烙,獲取所有數(shù)據(jù)的集合
val results: util.Iterator[Result] = scanner.iterator()
// 遍歷每一行的數(shù)據(jù)
for (result <- results) {
// 獲取每一行數(shù)據(jù)中所有的單元格
val cells: util.List[Cell] = result.listCells()
if (cells != null) {
// 遍歷所有的單元格,只能獲取最新的版本
for (cell <- cells) {
println(
s"""
|rowKey= ${Bytes.toString(CellUtil.cloneRow(cell))}
|columnFamily = ${Bytes.toString(CellUtil.cloneFamily(cell))}
|column = ${Bytes.toString(CellUtil.cloneQualifier(cell))}
|value = ${Bytes.toString(CellUtil.cloneValue(cell))}
""".stripMargin)
println("=======================================")
}
}
}
}
}
def getData(tableName: String, rowKey: String, columnFamily: String, column: String) = {
// 1. 獲取表
val tn: TableName = TableName.valueOf(tableName)
val table: Table = conn.getTable(tn)
// 2. 判斷表是否存在遣鼓,如果存在則進(jìn)行查詢啸盏,如果不存在,則直接退出方法骑祟,并提示client查詢的表不存在
if (tableExists(tableName)) {
val get = new Get(Bytes.toBytes(rowKey))
get.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(column))
val result: Result = table.get(get)
import scala.collection.JavaConversions._
val cells: util.List[Cell] = result.listCells()
for (cell <- cells) {
println(
s"""
|columnFamliy = ${Bytes.toString(CellUtil.cloneFamily(cell))}
|column = ${Bytes.toString(CellUtil.cloneQualifier(cell))}
|value = ${Bytes.toString(CellUtil.cloneValue(cell))}
|rowKey = ${Bytes.toString(CellUtil.cloneRow(cell))}
""".stripMargin)
}
} else {
println(s"{$tableName}表不存在")
}
}
/**
* 增加數(shù)據(jù)
*
* @param tableName
* @param rowKey
* @param columnFamily
* @param column
* @param value
*/
def putData(tableName: String, rowKey: String, columnFamily: String, column: String, value: String) = {
// 1. 獲取表
val tn: TableName = TableName.valueOf(tableName)
val table: Table = conn.getTable(tn)
// 2. 判斷表是否存在回懦,如果存在气笙,則添加數(shù)據(jù),如果不存在粉怕,則通知client
if (tableExists(tableName)) {
// 封裝插入的數(shù)據(jù)
val put = new Put(Bytes.toBytes(rowKey))
put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(column), Bytes.toBytes(value))
// 將數(shù)據(jù)插入
table.put(put)
// 關(guān)閉連接
table.close()
println("插入數(shù)據(jù)操作執(zhí)行成功")
} else {
println(s"當(dāng)前表{$tableName}不存在")
}
}
def tableExists(tableName: String) = {
val tn: TableName = TableName.valueOf(tableName)
admin.tableExists(tn)
}
/**
* 關(guān)閉連接
*/
def closeConn = {
// 3. 關(guān)閉對象
admin.close()
// 4. 關(guān)閉連接
conn.close()
}
}
六健民、HBase的優(yōu)化
6.1 預(yù)分區(qū)
1. 通過預(yù)分區(qū)的方式,使添加的數(shù)據(jù)進(jìn)入指定的region中贫贝,提前進(jìn)行數(shù)據(jù)分區(qū)秉犹。
2. 當(dāng)指定預(yù)分區(qū)的規(guī)則以后,那么默認(rèn)的region split分區(qū)原則則不會有效稚晚。
3. 每一個region維護(hù)著一個startkey和endkey崇堵,新增的數(shù)據(jù),根據(jù)數(shù)據(jù)的rowkey判斷進(jìn)入哪個region客燕,比較的方式:按照字典的順序鸳劳。
4. 在實(shí)際開發(fā)中,基本上所有的表都會進(jìn)行預(yù)分區(qū)也搓,且經(jīng)常是第一個或者是最后一個region中是沒有數(shù)據(jù)赏廓。
6.1.1 手動設(shè)定預(yù)分區(qū)
hbase> create 'staff1','info','partition1',SPLITS => ['1000','2000','3000','4000']
6.1.2 生成16進(jìn)制序列預(yù)分區(qū)
create 'staff2','info','partition2',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
6.1.3 按照文件設(shè)置預(yù)分區(qū)
#文件內(nèi)容
aaaa
bbbb
cccc
dddd
create 'staff3','partition3',SPLITS_FILE => 'splits.txt'
6.1.4 使用API設(shè)置
//自定義算法,產(chǎn)生一系列Hash散列值存儲在二維數(shù)組中
byte[][] splitKeys = 某個散列值函數(shù)
//創(chuàng)建HbaseAdmin實(shí)例
HBaseAdmin hAdmin = new HBaseAdmin(HbaseConfiguration.create());
//創(chuàng)建HTableDescriptor實(shí)例
HTableDescriptor tableDesc = new HTableDescriptor(tableName);
//通過HTableDescriptor實(shí)例和散列值二維數(shù)組創(chuàng)建帶有預(yù)分區(qū)的Hbase表
hAdmin.createTable(tableDesc, splitKeys);
6.2 RowKey設(shè)計(jì)
-- 1. 現(xiàn)狀
在hbase中傍妒,rowkey是唯一區(qū)分?jǐn)?shù)據(jù)進(jìn)入哪個region中幔摸,如果region設(shè)計(jì)合理,那么有可能導(dǎo)致數(shù)據(jù)冗余和數(shù)據(jù)傾斜颤练。
-- 2. 設(shè)計(jì)規(guī)則
1. 長度:
a既忆、rowkey理論字節(jié)數(shù)在10-100字節(jié)之間最好,一是可以表述較多的數(shù)據(jù)內(nèi)容嗦玖,其次是數(shù)據(jù)不會過多患雇;
b、數(shù)據(jù)長度最好是2的n次冪宇挫;
c苛吱、rowkey的數(shù)據(jù)長度最好相同;
2. 散列:
a器瘪、對rowkey進(jìn)行散列又谋,采用md5,hash等方式娱局,以防止數(shù)據(jù)傾斜继阻;
3. 唯一性:防止rowkey相同。
-- 3. 實(shí)現(xiàn)方式:
a、字符串反轉(zhuǎn)奏赘。如:
20170524000001轉(zhuǎn)成10000042507102
20170524000002轉(zhuǎn)成20000042507102
b缚柳、字符串連接:將本來放進(jìn)列的數(shù)據(jù),放到rowkey中抡谐,主要是將經(jīng)常需要使用到的字段/不發(fā)生改變的數(shù)據(jù)拼接到rowkey中音五。
如:rowkey:id_name_age
6.3 內(nèi)存優(yōu)化
HBase操作過程中需要大量的內(nèi)存開銷,畢竟Table是可以緩存在內(nèi)存中的碟绑,但是不建議分配非常大的堆內(nèi)存,因?yàn)镚C過程持續(xù)太久會導(dǎo)致RegionServer處于長期不可用狀態(tài)苗桂,一般16~36G內(nèi)存就可以了放案,如果因?yàn)榭蚣苷加脙?nèi)存過高導(dǎo)致系統(tǒng)內(nèi)存不足稿湿,框架一樣會被系統(tǒng)服務(wù)拖死
6.4 基礎(chǔ)優(yōu)化
-- 1.Zookeeper會話超時時間
'配置文件':hbase-site.xml
'屬性':zookeeper.session.timeout
'解釋':默認(rèn)值為90000毫秒(90s)。當(dāng)某個RegionServer掛掉缎罢,90s之后Master才能察覺到萎坷。可適當(dāng)減小此值瓜浸,以加快Master響應(yīng)杠巡,可調(diào)整至600000毫秒。
--2.設(shè)置RPC監(jiān)聽數(shù)量
'配置文件':hbase-site.xml
'屬性':hbase.regionserver.handler.count
'解釋':默認(rèn)值為30雇寇,用于指定RPC監(jiān)聽的數(shù)量氢拥,可以根據(jù)客戶端的請求數(shù)進(jìn)行調(diào)整,讀寫請求較多時锨侯,增加此值嫩海。
--3.手動控制Major Compaction
'配置文件':hbase-site.xml
'屬性':hbase.hregion.majorcompaction
'解釋':默認(rèn)值:604800000秒(7天), Major Compaction的周期识腿,若關(guān)閉自動Major Compaction出革,可將其設(shè)為0
在實(shí)際開發(fā)中,我們一般設(shè)置為0渡讼,然后通過azkaban進(jìn)行調(diào)度骂束。
--4.優(yōu)化HStore文件大小
'配置文件':hbase-site.xml
'屬性':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栖榨。
--5.優(yōu)化HBase客戶端緩存
'配置文件':hbase-site.xml
'屬性':hbase.client.write.buffer
'解釋':默認(rèn)值2097152bytes(2M)用于指定HBase客戶端緩存昆汹,增大該值可以減少RPC調(diào)用次數(shù),但是會消耗更多內(nèi)存婴栽,反之則反之满粗。一般我們需要設(shè)定一定的緩存大小,以達(dá)到減少RPC次數(shù)的目的愚争。
--6.指定scan.next掃描HBase所獲取的行數(shù)
'配置文件':hbase-site.xml
'屬性':hbase.client.scanner.caching
'解釋':用于指定scan.next方法獲取的默認(rèn)行數(shù)映皆,值越大,消耗內(nèi)存越大轰枝。
--7.BlockCache占用RegionServer堆內(nèi)存的比例
'配置文件':hbase-site.xml
'屬性':hfile.block.cache.size
'解釋':默認(rèn)0.4捅彻,讀請求比較多的情況下,可適當(dāng)調(diào)大
--8.MemStore占用RegionServer堆內(nèi)存的比例
'配置文件':hbase-site.xml
'屬性':hbase.regionserver.global.memstore.size
'解釋':默認(rèn)0.4鞍陨,寫請求較多的情況下步淹,可適當(dāng)調(diào)大
七、與Hive的集成
7.1 HBase與Hive的對比
--1. Hive
1. 是一個數(shù)據(jù)倉庫:Hive的本質(zhì)其實(shí)就是相當(dāng)于將HDFS中已經(jīng)存儲的文件在Mysql中做一個雙射關(guān)系湾戳,以方便使用HQL去管理查詢贤旷;
2. 用于數(shù)據(jù)分析、清洗
3. 基于HDFS 砾脑、 MapReduce: Hive存儲的數(shù)據(jù)依舊在DataNode上幼驶,編寫的HQL語句終將轉(zhuǎn)換為MapReduce代碼執(zhí)行;
--2. HBase
1. 是數(shù)據(jù)庫
2. 用于存儲結(jié)構(gòu)化和非結(jié)構(gòu)化數(shù)據(jù)
3. 基于HDFS
4. 延遲低韧衣。接入在線業(yè)務(wù)使用
7.2 HBase 與 Hive集成使用
- 在hive-site.xml中添加zookeeper的屬性盅藻,如下:
<property>
<name>hive.zookeeper.quorum</name>
<value>hadoop105,hadoop106,hadoop107</value>
</property>
<property>
<name>hive.zookeeper.client.port</name>
<value>2181</value>
</property>
7.3 案例一
-- 需求:在Hive中創(chuàng)建的表,在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)的表
- 在Hive中創(chuàng)建臨時中間表硕噩,用于load文件中的數(shù)據(jù)
建立上訴的表假残,不能直接通過load的方式加載數(shù)據(jù),需要通過查詢其他表的數(shù)據(jù)進(jìn)行插入
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';
- 向Hive中間表中l(wèi)oad數(shù)據(jù)
load data local inpath '/opt/module/hive/datas/emp' into table emp;
- 通過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
hive> select * from hive_hbase_emp_table;
-- HBase
Hbase> scan 'hbase_emp_table'
7.4 案例二
-- 需求:在HBase中已經(jīng)存儲了某一張表hbase_emp_table炉擅,然后在Hive中創(chuàng)建一個外部表來關(guān)聯(lián)HBase中的hbase_emp_table這張表
在案例1的基礎(chǔ)上進(jìn)行完成辉懒。
- 在Hive中創(chuàng)建外部表
CREATE EXTERNAL TABLE relevance_hbase_emp(
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");
- 關(guān)聯(lián)后就可以使用Hive函數(shù)進(jìn)行一些分析操作了
hive (default)> select * from relevance_hbase_emp;
八、Phoenix
8.1 Phoenix簡介
-- 1. Phoenix是什么谍失?
可以理解是HBase的開源SQL皮膚眶俩,可以使用標(biāo)準(zhǔn)的JDBC API代替HBase客戶端API來創(chuàng)建表,插入數(shù)據(jù)和查詢HBase數(shù)據(jù)快鱼。
-- 2. Phoenix中創(chuàng)建的表存儲在HBase中颠印。
8.2 Phoenix特點(diǎn)
1. 容易集成:如Spark 纲岭、 Hive 、 Pig 线罕、 Flume 止潮、 MAPReduce
2. 操作容易:DML命令以及通過DDL命令來對表進(jìn)行操作
3. 支持HBase的二級索引創(chuàng)建。
8.3 Phoenix架構(gòu)
image.png
8.4 Phoenix安裝
8.4.1 官網(wǎng)地址
http://phoenix.apache.org/
8.4.2 Phoenix部署
- 上傳并解壓tar包并修改名字
[atguigu@hadoop105 module]$ tar -zxvf apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz -C /opt/module/
[atguigu@hadoop102 module]$ mv apache-phoenix-5.0.0-HBase-2.0-bin phoenix
- 復(fù)制server包并拷貝到各個節(jié)點(diǎn)的hbase/lib
[atguigu@hadoop102 module]$ cd /opt/module/phoenix/
[atguigu@hadoop102 phoenix]$ cp /opt/module/phoenix/phoenix-5.0.0-HBase-2.0-server.jar /opt/module/hbase/lib/
[atguigu@hadoop102 phoenix]$ xsync /opt/module/hbase/lib/phoenix-5.0.0-HBase-2.0-server.jar
- 配置環(huán)境變量
#phoenix
export PHOENIX_HOME=/opt/module/phoenix
export PHOENIX_CLASSPATH=$PHOENIX_HOME
export PATH=$PATH:$PHOENIX_HOME/bin
- 重啟HBase
[atguigu@hadoop102 ~]$ stop-hbase.sh
[atguigu@hadoop102 ~]$ start-hbase.sh
- 連接Phoenix
[atguigu@hadoop101 phoenix]$ /opt/module/phoenix/bin/sqlline.py hadoop102,hadoop103,hadoop104:2181
8.5 Phoenix Shell 操作
--說明:
1钞楼、 在Phoenix創(chuàng)建的表沽翔,都是存儲在hbase中;
2窿凤、 Phoenix表的主鍵,在hbase表中跨蟹,相當(dāng)于rowkey雳殊,所以在Phoenix建表時,至少設(shè)置一個主鍵窗轩,且數(shù)據(jù)類型最好設(shè)置為varchar夯秃;
3、 在Phoenix創(chuàng)建表時痢艺,表名和字段自動會變成大寫仓洼,如果想要設(shè)置為小寫,那么需要使用'雙引號'堤舒;
4色建、 '單引號'表示字符串;
5舌缤、 在HBase創(chuàng)建的表箕戳,默認(rèn)在Phoenix是查詢不到的,需要通過映射的方式可以查詢国撵。
6陵吸、 Phoenix中不支持insert語法,使用了'upsert'代替;
7介牙、 Phoenix的shell操作壮虫,很多和sql語法相似,但是也有些語法時不支持的环础;
8囚似、 Phoenix創(chuàng)建的表,在hbase默認(rèn)只有一個列族'0'喳整;
8.5.1 顯示所有表
!table 或 !tables
8.5.2 創(chuàng)建表
create table lianzp (id varchar primary key , age varchar )
- 聯(lián)合主鍵
指定多個列的聯(lián)合作為RowKey
CREATE TABLE IF NOT EXISTS us_population (
State CHAR(2) NOT NULL,
City VARCHAR NOT NULL,
Population BIGINT
CONSTRAINT my_pk PRIMARY KEY (state, city));
8.5.3 插入數(shù)據(jù)
upsert into student values('1001','zhangsan','beijing');
8.5.4 查詢記錄
select * from student;
select * from student where id='1001';
8.5.5 刪除記錄
delete from student where id='1001';
8.5.6 刪除表
drop table student;
8.5.7 退出命令行
!quit
8.6 Phoenix與hbase表映射
-- 1. 說明:
a. 在Phoenix創(chuàng)建的表可以在hbase中直接查詢到谆构,因?yàn)镻hoenix創(chuàng)建的表就是在hbase上;
b. 在hbase的表框都,Phoenix默認(rèn)不能直接看到搬素,需要使用映射的方式才能看到呵晨。
-- 2. 序列化問題:
a、Phoenix數(shù)據(jù)的序列化器和hbase數(shù)據(jù)序列化器不一致熬尺;
b摸屠、Phoenix使用自身的序列化器,而hbase使用的是bytes.toBtes()對數(shù)據(jù)進(jìn)行序列化粱哼,則導(dǎo)致從Phoenix讀取hbase和從hbase讀取Phoenix數(shù)據(jù)時季二,會出現(xiàn)讀出的數(shù)據(jù)和原表中的數(shù)據(jù)不一致。現(xiàn)象為:
-- 1). Hbase讀取Phoenix表
a揭措、'列名格式問題':列名將Phoenix的字段轉(zhuǎn)換為16進(jìn)制顯示
b胯舷、'value格式問題':值類型數(shù)據(jù)也被轉(zhuǎn)換成了16進(jìn)制顯示
-- 2). Phoenix讀取hbase表
a、'列名格式問題':在Phoenix創(chuàng)建的字段和hbase表中的字段一樣绊含,但是沒有數(shù)據(jù)
b桑嘶、'value格式問題':查詢的數(shù)據(jù)和hbase的數(shù)據(jù)完全不等,描述見下圖:
-- 3. 解決方案如下:
8.6.1 Phoenix創(chuàng)建的表
-- 1. 在Phoenix創(chuàng)建的表躬充,在hbase中可以查詢到逃顶,但是會發(fā)現(xiàn)多了每行數(shù)據(jù)中,會多如下一列數(shù)據(jù)充甚,'稱之為空/虛的keyvalue':
1001 column=0:_0, timestamp=1593071947795, value=x
-- 2. 為什么Phoenix在進(jìn)行upsert時會添加一個空/虛擬KeyValue以政?
在hbase表中,rowkey對應(yīng)Phoenix表中的主鍵伴找,如果Phoenix中表只有主鍵盈蛮,沒有其他列,那么在habse的表中疆瑰,就只有rowkey眉反,沒有列族了。所有通過增加這樣一列空的列穆役,確保這行數(shù)據(jù)即有rowkey寸五,也有列族.
-- 3. 官網(wǎng)說明:
8.6.2 HBase創(chuàng)建的表
-- 1. 在HBase表映射方式有兩種:視圖映射和表映射
-- 2. 在Phoenix實(shí)現(xiàn)映射的方式:
a、創(chuàng)建的表名和hbase的表名相同耿币,注意大小寫梳杏;
b、創(chuàng)建一個主鍵淹接,用來接收hbase表中的rowkey十性;
c、其余的字段塑悼,聲明方式為:hbase表中的:列族.列名
d劲适、最后需要加上:column_encoded_bytes=0,使的Phoenix反序列器為bytes.toBytes()厢蒜,與hbase序列化器一致霞势,則Phoenix可以
找到hbase表中的列烹植。
-- 3. 說明:
a、創(chuàng)建時表名和字段名一定要相同愕贡;
b草雕、如果創(chuàng)建的字段在hbase表不存在,也是可以的固以。相當(dāng)于空列
8.6.2.1 視圖映射
-- 1. 創(chuàng)建的視圖是只讀墩虹,只能用來進(jìn)行查詢,無法通過視圖對原數(shù)據(jù)進(jìn)行修改等操作憨琳。
- 在hbase準(zhǔn)備數(shù)據(jù)
create 'lianzp' ,'info'诫钓,'info1'
put 'lianzp','1001','info:name','zs'
- Phoenix端操操作
-- 創(chuàng)建視圖
create view "lianzp" (id varchar primary key , "info"."name" varchar , "info1"."address" varchar ) column_encoded_bytes=0;
-- 刪除視圖
drop view "lianzp";
8.6.2.2 表映射
-- 只需要將將create view 改成create table 即可。
8.7 二級索引
8.7.1 修改配置
- 添加如下配置到HBase的HRegionserver節(jié)點(diǎn)的hbase-site.xml
<!-- phoenix regionserver 配置參數(shù)-->
<property>
<name>hbase.regionserver.wal.codec</name>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>
<property>
<name>hbase.region.server.rpc.scheduler.factory.class</name>
<value>org.apache.hadoop.hbase.ipc.PhoenixRpcSchedulerFactory</value>
<description>Factory to create the Phoenix RPC Scheduler that uses separate queues for index and metadata updates</description>
</property>
<property>
<name>hbase.rpc.controllerfactory.class</name>
<value>org.apache.hadoop.hbase.ipc.controller.ServerRpcControllerFactory</value>
<description>Factory to create the Phoenix RPC Scheduler that uses separate queues for index and metadata updates</description>
</property>
8.7.2 全局二級索引
-- 1. 創(chuàng)建全局索引時篙螟,會在HBase中建立一張新表
- 創(chuàng)建單個字段的全局索引
CREATE INDEX my_index ON my_table (my_col);
'如果想查詢的字段不是索引字段的話,索引表不會被使用尖坤,也就是說不會帶來查詢速度的提升
- 創(chuàng)建攜帶其他字段的全局索引
CREATE INDEX my_index ON my_table (v1) INCLUDE (v2);
image-20200629011141184
8.7.3 局部二級索引
-- 1. 索引數(shù)據(jù)和數(shù)據(jù)表的數(shù)據(jù)是存放在同一張表中(且是同一個Region)
- 創(chuàng)建局部索引
CREATE LOCAL INDEX my_index ON my_table (my_column);
8.7.4 局部和全局的選擇
-- 1. 兩種索引的介紹
全局索引:會單獨(dú)創(chuàng)建一個新的文件,默認(rèn)是一個region闲擦,同時會采用默認(rèn)的region split的切分規(guī)則;
局部索引:在原數(shù)據(jù)表的插入數(shù)據(jù)场梆,索引數(shù)據(jù)和數(shù)據(jù)表的數(shù)據(jù)是存放在同一張表中(且是同一個Region)墅冷。
-- 2. 在需要創(chuàng)建索引時,我們是選擇創(chuàng)建哪種索引呢或油?
創(chuàng)建索引以后寞忿,每次數(shù)據(jù)的改動都需要更新索引表。
兩種索引選擇的規(guī)則如下:
'情況1':寫操作頻繁顶岸,則選擇局部索引腔彰,因?yàn)閿?shù)據(jù)和索引在同一張表的同一個region中,所以更新索引的數(shù)據(jù)就不需要跨節(jié)點(diǎn)辖佣,
避免了在寫操作的時候往不同服務(wù)器的索引表中寫索引帶來的額外開銷;
'情況2': 讀操作頻繁時霹抛,則選擇全局索引,因?yàn)槿炙饕锌梢灾苯佣ㄎ坏綌?shù)據(jù)卷谈,效率高杯拐。
8.8 Phoenix JDBC操作
8.8.1 啟動query server
queryserver.py start
8.8.2 創(chuàng)建項(xiàng)目并導(dǎo)入依賴
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.phoenix/phoenix-queryserver-client -->
<dependency>
<groupId>org.apache.phoenix</groupId>
<artifactId>phoenix-queryserver-client</artifactId>
<version>5.0.0-HBase-2.0</version>
</dependency>
</dependencies>
8.8.3 編寫代碼
package com.atguigu;
import java.sql.*;
import org.apache.phoenix.queryserver.client.ThinClientUtil;
public class PhoenixTest {
public static void main(String[] args) throws SQLException {
String connectionUrl = ThinClientUtil.getConnectionUrl("hadoop105", 8765);
System.out.println(connectionUrl);
Connection connection = DriverManager.getConnection(connectionUrl);
PreparedStatement preparedStatement = connection.prepareStatement("select * from student");
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
System.out.println(resultSet.getString(1) + "\t" + resultSet.getString(2));
}
//關(guān)閉
connection.close();
}
}