1. HBase存儲(chǔ)中的3個(gè)核心機(jī)制
1.flush機(jī)制:
當(dāng)MemStore達(dá)到閾值之后,會(huì)flush成一個(gè)StoreFile (也就是內(nèi)存中的數(shù)據(jù)寫(xiě)入了磁盤(pán))。
2.compact機(jī)制:
當(dāng)StoreFile達(dá)到閾值時(shí),合并StoreFile。合并過(guò)程中cell版本合并和數(shù)據(jù)刪除。
3.split機(jī)制:
當(dāng)region不斷增大,達(dá)到閾值,region會(huì)分成兩個(gè)新的region。
2.HBase Java API使用
1. 為HBase Java編程準(zhǔn)備開(kāi)發(fā)環(huán)境
使用java進(jìn)行HBase編程,pom文件需要添加2個(gè)依賴,分別是hbase-client 、 hbase-server(可以沒(méi)有)、hadoop-client。
查看HBase的API文檔,以下幾個(gè)類是比較常用的,需要熟悉:
HBaseAdmin (管理操作、創(chuàng)建表等)
HTable (操作表、上傳數(shù)據(jù))
Configuration
HBase的java API開(kāi)發(fā)與HBase shell的命令基本上是一致的。例如shell中的put操作,對(duì)應(yīng)著API中的Put對(duì)象。
2. 添加IDE依賴的jar包:
HBase的開(kāi)發(fā),需要在pom.xml中引入 hbase-client、hbase-server和hadoop-client幾個(gè)依賴包(選取的版本是:hadoop: 2.5.0 ; hbase:0.98.6-hadoop2),如下是pom.xml定義:
<properties>
<hadoop.version>2.5.0</hadoop.version>
<hbase.version>0.98.6-hadoop2</hbase.version>
</properties>
<dependencies>
<!-- hbase client -->
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>${hbase.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>${hbase.version}</version>
</dependency>
<!-- hadoop client -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
</dependencies>
3. 添加配置文件:
需要將HBase和Hadoop的配置文件,添加到項(xiàng)目的Classpath。新建“Source Folder”:src/main/resources,將hadoop和hbase安裝目錄下的配置文件挪到這個(gè)目錄下(主要是core-site.xml、hdfs-site.xml和hbase-site.xml):

4. 獲取HBase配置環(huán)境信息:
在使用Java API時(shí),Client端需要知道HBase的環(huán)境配置,使用Configuration類來(lái)封裝該信息。
Configuration conf = HBaseConfiguration.create();
在創(chuàng)建該對(duì)象時(shí),首先在classpath中尋找hbase-site.xml文件來(lái)讀取配置,如果不存在該文件,則尋找hbase-default.xml文件來(lái)讀取配置(也就是默認(rèn)配置)。
5. Get獲取數(shù)據(jù):
使用Get對(duì)象獲取數(shù)據(jù):
// 抽象的獲取HTable對(duì)象的靜態(tài)方法,返回HTable對(duì)象
public static HTable getTable (String tablename) throws IOException {
Configuration conf = HBaseConfiguration.create();
HTable table = new HTable(conf,tablename);
return table;
}
//根據(jù)row_key返回這條記錄中的信息
public static void getByRowKey(String tablename,String rowkey)
throws IOException
{
HTable table = HBaseAPIOper.getTable(tablename);
//Get Object is bound to one row(identified by rowkey)
Get get = new Get(Bytes.toBytes(rowkey));
Result rs = table.get(get);
Cell[] cells = rs.rawCells();
for (Cell cell : cells){
System.out.print( Bytes.toString(CellUtil.cloneFamily(cell)) + ":" );
System.out.print( Bytes.toString(CellUtil.cloneQualifier(cell)) + "=>" );
System.out.print( Bytes.toString(CellUtil.cloneValue(cell)) + "\n" );
}
table.close();
}
//返回某個(gè)row_key、某個(gè)列族、某個(gè)列的數(shù)據(jù)(只返回指定列的信息)
public static void getByColumns (String tablename,String rowkey,
String family,String qualifier) throws IOException{
HTable table = HBaseAPIOper.getTable(tablename);
//Get Object is bound to one row(identified by rowkey)
Get get = new Get(Bytes.toBytes(rowkey));
get.addColumn(Bytes.toBytes(family), Bytes.toBytes(qualifier));
Result rs = table.get(get);
Cell[] cells = rs.rawCells();
for (Cell cell : cells){
System.out.print( Bytes.toString(CellUtil.cloneFamily(cell)) + ":" );
System.out.print( Bytes.toString(CellUtil.cloneQualifier(cell)) + "=>" );
System.out.print( Bytes.toString(CellUtil.cloneValue(cell)) + "\n" );
}
table.close();
}
Get get = new Get(rowkey的字節(jié)數(shù)組)。 Get對(duì)象和rowkey進(jìn)行綁定。Result result = HTable對(duì)象.get(Get對(duì)象)。下面遍歷結(jié)果集,使用rawCells()方法,獲取所有查到的單元格信息。CellUtil類,是用于操作單元格的工具類,CellUtil.cloneFamily()獲取列族信息,CellUtil.cloneQualifier()獲取列信息,CellUtil.cloneValue()獲取對(duì)應(yīng)的值。為了防止資源使用崩潰,一定要記得關(guān)閉table對(duì)象。
同時(shí),也可以通過(guò)操作Get對(duì)象,來(lái)限定只顯示部分列。get.addColumn("列族","列名")
6. Put插入數(shù)據(jù):
實(shí)體信息的插入,通過(guò)put操作完成。
// Put rows by rowkey (Auto Generated!!)
public static void putRows(String tablename) throws IOException{
HTable table = HBaseAPIOper.getTable(tablename);
long rowkey = System.currentTimeMillis();
// The Integer rowkey must be cast to String.
Put put0 = new Put(Bytes.toBytes(Long.toString(rowkey)));
put0.add(
Bytes.toBytes("info"),
Bytes.toBytes("name"),
Bytes.toBytes("natty")
) ;
put0.add(
Bytes.toBytes("info"),
Bytes.toBytes("age"),
Bytes.toBytes("31")
) ;
// The Integer rowkey must be cast to String.
Put put1 = new Put(Bytes.toBytes(Long.toString(rowkey + 1)));
put1.add(
Bytes.toBytes("info"),
Bytes.toBytes("name"),
Bytes.toBytes("natty")
) ;
put1.add(
Bytes.toBytes("info"),
Bytes.toBytes("age"),
Bytes.toBytes("31")
) ;
ArrayList<Put> list = new ArrayList<Put>();
list.add(put0);
list.add(put1);
table.put(list);
table.close();
}
put操作對(duì)應(yīng)于Put對(duì)象。創(chuàng)建Put對(duì)象, Put put = new Put(rowkey對(duì)應(yīng)的字節(jié)數(shù)組);之后添加列信息,put.add("列族","列名","列值"),通過(guò)多次put.add()可以一次添加多個(gè)列值信息。將列信息添加到表格,表格對(duì)象.put(Put對(duì)象),或者添加多個(gè)put對(duì)象,表格對(duì)象.put(含有多個(gè)Put對(duì)象的list集合)。
7. Delete刪除數(shù)據(jù)和Update數(shù)據(jù):
刪除數(shù)據(jù)需要使用Delete對(duì)象, Delete delete = new Delete(行鍵對(duì)應(yīng)的字節(jié)數(shù)組)。指定要?jiǎng)h除的列(列族),delete.deleteColumn("列族","列名")
public static void deleteByRowkey(String tablename,String rowkey)
throws IOException{
HTable table = HBaseAPIOper.getTable(tablename);
Delete del = new Delete(Bytes.toBytes(rowkey));
//delete all the family
del.deleteFamily(Bytes.toBytes("info"));
//delete just one column
del.deleteColumn(Bytes.toBytes("info"), Bytes.toBytes("name"));
table.delete(del);
table.close();
}
HBase中Update操作也使用Put對(duì)象完成,操作方法與插入操作完全一致。
8. Scan操作:
ResultScanner是由查詢結(jié)果Result對(duì)象組成的,可以通過(guò)2次遍歷獲取數(shù)據(jù)。getRow()獲得行鍵。另外,也可以使用IOUtils.closeStream()來(lái)關(guān)閉table對(duì)象等資源。通過(guò)setStartRow()和setStopRow()方法,限定scan的查詢范圍。通過(guò)scan.addFamily()或者scan.addColumn()增加通過(guò)列族/列篩選數(shù)據(jù)。
public static void scanByTable(String tablename) throws IOException
{
HTable table = HBaseAPIOper.getTable(tablename);
Scan scan = new Scan();
//make the scope of scan.
// >= setStartRow() AND < setStopRow()
scan.setStartRow(Bytes.toBytes("10001"));
scan.setStopRow(Bytes.toBytes("10004"));
//filter the data through Family or Column
scan.addFamily(Bytes.toBytes("info"));
scan.addColumn(Bytes.toBytesBinary("info"), Bytes.toBytes("name"));
ResultScanner resultscanner = table.getScanner(scan);
for (Result result:resultscanner)
{
// getRow() method can get the rowkey.
System.out.println(Bytes.toString(result.getRow()));
Cell[] cells = result.rawCells();
for (Cell cell : cells){
System.out.print( Bytes.toString(CellUtil.cloneFamily(cell)) + ":" );
System.out.print( Bytes.toString(CellUtil.cloneQualifier(cell)) + "=>" );
System.out.print( Bytes.toString(CellUtil.cloneValue(cell)) + "\n" );
}
}
IOUtils.closeStream(resultscanner);
IOUtils.closeStream(table);
}
3.HBase架構(gòu)深入總結(jié)
1.客戶端作用:
1.整個(gè)HBase集群的訪問(wèn)入口;
2.使用HBase RPC機(jī)制與HMaster 和 HRegionServer通信。
3.與HMaser通信進(jìn)行管理類操作。
4.與HRegionServer通信進(jìn)行數(shù)據(jù)讀寫(xiě)類操作。
2.Zookeeper作用:
1.實(shí)現(xiàn)HMaster的HA,保證集群中只有一個(gè)HMaster。
2.保存root region的位置(meta表),Region的尋址入口。
客戶端在訪問(wèn)表時(shí),需要找到表的每個(gè)region對(duì)應(yīng)的RegionServer(管理Region的角色)。Root(新版本是meta)表存儲(chǔ)了Region的分布情況以及每個(gè)Region的詳細(xì)信息。
3.實(shí)時(shí)監(jiān)控RegionServer的上下線信息,并及時(shí)通知Master。
4.存儲(chǔ)HBase的schema和table元數(shù)據(jù)(有哪些表、每個(gè)表有哪些列族)。
3.HMaster作用和特點(diǎn):
1.HMaster作用:
(1). HMaster負(fù)責(zé)Table和Region的管理工作,為RegionServer分配Region。
(2). 管理Region Server的負(fù)載均衡(為每一個(gè)Region Server分配多少Region),調(diào)整Region分布。
(3). 當(dāng)某一個(gè)Region Server停機(jī)下線后,負(fù)責(zé)失效Region Server上的Region的遷移。
(4). 監(jiān)聽(tīng)zookeeper,基于zookeeper來(lái)得知Region Server上下線。
(5). 監(jiān)聽(tīng)zookeeper,基于zookeeper來(lái)保證Master的HA
2.HMaster的特點(diǎn):
(1)HMaster沒(méi)有單點(diǎn)故障問(wèn)題(SPOF:Single Point Of Failure)。HBase可以啟動(dòng)多個(gè)HMaster,Zookeeper保證只有一個(gè)HMaster運(yùn)行。
啟動(dòng)backup master節(jié)點(diǎn)方法:在conf下修改配置文件backup-masters,通過(guò)hbase-daemons.sh腳本啟動(dòng)backup master。
$bin/hbase-daemons.sh start master-backup
(2)不參與客戶端數(shù)據(jù)讀寫(xiě)
(3)HMaster的負(fù)載很低(在同一個(gè)主機(jī)上可以和NN、SNN等服務(wù)共同運(yùn)行)。
4.Region Server的作用:
- 維護(hù)Region(HMaster分配的),響應(yīng)客戶端的IO訪問(wèn)請(qǐng)求(讀寫(xiě)),向HDFS讀寫(xiě)數(shù)據(jù)。
- 負(fù)責(zé)處理region的 flush、compact、split 幾個(gè)過(guò)程。
- 維護(hù)region的cache

4.HBase數(shù)據(jù)檢索三種方式
HBase的數(shù)據(jù)檢索,主要提供了3種方式,分別是:Get方式檢索、Scan范圍檢索、Scan全表掃描。
- Get方式:
Get需要使用rowkey作為參數(shù)檢索。
在hbase shell中使用get命令
在java API中使用Get對(duì)象。 - Scan 范圍搜索:
依據(jù)rowkey匹配查找,默認(rèn)根據(jù)rowkey匹配
在hbase shell中使用scan命令
在java API中使用Scan對(duì)象。 - Scan 全部掃描:
全表掃描
和MapReduce/Spark一起使用。
5.HBase與MapReduce集成
1.HBase和MapReduce的整合場(chǎng)景:
第一種應(yīng)用場(chǎng)景: 文本文檔txt或者csv文件導(dǎo)入到HBase中。
第二種應(yīng)用場(chǎng)景: HBase的表導(dǎo)成另外一個(gè)表。
Data Source(數(shù)據(jù)源): 。本地、HBASE表、HDFS
Data Sink: 數(shù)據(jù)寫(xiě)進(jìn)哪個(gè)地方。
Data Source & Data Sink:
2.HBase和MapReduce的原因和方法:
- 為什么使用MapReduce導(dǎo)入:
MapReduce是并行處理框架,比較快。 - HBase和MapReduce整合的方法:
使用如下命令可以返回hbase的classpath的jar包:
$ bin/hbase mapredcp
查看命令的具體含義:
$ bin/hbase
3.HBase的默認(rèn)MapReduce程序:
HBase默認(rèn)集成了一些mapreduce程序。這些程序存放在這個(gè)jar包(HBase的安裝目錄下)中 :
/opt/modules/hbase-0.98.6-cdh5.3.6/lib/hbase-server-0.98.6-cdh5.3.6.jar
下面,我們執(zhí)行這個(gè)jar包:
(1) 導(dǎo)入HBASE_CLASS path環(huán)境變量:
export HADOOP_HOME=/opt/modules/hadoop-2.5.0-cdh5.3.6
export HBASE_HOME=/opt/modules/hbase-0.98.6-cdh5.3.6
export HADOOP_CLASSPATH=`$HBASE_HOME/bin/hbase mapredcp`
上邊步驟設(shè)置HBase的CLASSPATH。變量引入,一般寫(xiě)進(jìn)shell腳本,跟隨job任務(wù)執(zhí)行的命令一起。如果要寫(xiě)入/etc/profile 會(huì)永久生效,并且需要加export。
(2)通過(guò)yarn執(zhí)行mapreduce的jar包:
查看整個(gè)jar包所提供的所有功能:
$ /opt/modules/hadoop-2.5.0-cdh5.3.6/bin/yarn jar /opt/modules/hbase-0.98.6-cdh5.3.6/lib/hbase-server-0.98.6-cdh5.3.6.jar

(3)通過(guò)mapreduce查看某個(gè)表的行數(shù)(rowkey的個(gè)數(shù)):
$ /opt/modules/hadoop-2.5.0-cdh5.3.6/bin/yarn jar /opt/modules/hbase-0.98.6-cdh5.3.6/lib/hbase-server-0.98.6-cdh5.3.6.jar rowcounter 'user'
4.自定義HBase的MapReduce程序:
5.HBase數(shù)據(jù)遷移工具importTsv:
HBase存在較多數(shù)據(jù)遷移的場(chǎng)景:例如,需要將一個(gè)數(shù)據(jù)文件直接導(dǎo)入到HBase表中,還有將生產(chǎn)集群的數(shù)據(jù)遷移到測(cè)試集群等等。
ImportTsv是HBase官方提供的基于MapReduce的批量數(shù)據(jù)導(dǎo)入工具。ImportTsv是一個(gè)命令行工具,可以將存儲(chǔ)在HDFS上的自定義分隔符(默認(rèn)\t)的數(shù)據(jù)文件,通過(guò)一條命令導(dǎo)入到HBase表中,非常適合大數(shù)據(jù)量導(dǎo)入。
下面運(yùn)行一個(gè)實(shí)例來(lái)演示導(dǎo)入過(guò)程:
(1)創(chuàng)建一個(gè)將要導(dǎo)入的測(cè)試文件:
(2)上傳該文件到HDFS上的指定目錄:
(3)HBase建表:
(4)開(kāi)始運(yùn)行MapReduce執(zhí)行導(dǎo)入。
(5)驗(yàn)證結(jié)果。
6. 使用BulkLoad加載數(shù)據(jù):
6.創(chuàng)建HBase表
創(chuàng)建HBase有常見(jiàn)的三種方式:
1.創(chuàng)建表時(shí),指定region劃分方式(根據(jù)rowkey)
create 'tb1','fa1',SPLITS => ['10','20','30','40']
tb1表有五個(gè)Region。SPLITS根據(jù)rowkey來(lái)將記錄分配到各自的region中:

2.創(chuàng)建表時(shí),通過(guò)文件來(lái)將表劃分為多分區(qū)(最常用方式):
將分區(qū)范圍放在一個(gè)物理文件中,然后在創(chuàng)建表時(shí)指定這個(gè)文件。例如定義下面一個(gè)regions.txt文件:
20170605
20170606
20170607
20170608
20170609
20170610
下面創(chuàng)建表,根據(jù)這個(gè)文件指定劃分region。
create table 'tb2','fa2',SPLITS_FILE => '/home/natty/hbase/regions.txt',OWNER => 'natty'
需要注意,這里建議使用絕對(duì)路徑來(lái)指定文件。
3.指定分區(qū)的數(shù)量,創(chuàng)建表:
create table 'tb3','fa3',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
如果指定region的起止范圍,系統(tǒng)可以自動(dòng)定義。
7. HBase的RowKey設(shè)計(jì)
1.表結(jié)構(gòu)設(shè)計(jì):
最核心的內(nèi)容是設(shè)計(jì)rowkey。設(shè)計(jì)rowkey的要點(diǎn):
1.根據(jù)需求設(shè)計(jì),查詢速度快。
2.盡量覆蓋更多業(yè)務(wù)場(chǎng)景,增加復(fù)用的可能性。
3.離線設(shè)計(jì),避免熱點(diǎn)
2.案例設(shè)計(jì):
下面介紹一個(gè)實(shí)例,訂單歷史表。這個(gè)表有以下的查詢場(chǎng)景。