[TOC]
1 hbase java api的使用
hbase的java api在windows中使用,第一次連接時,會有點慢被饿,大概需要二十秒左右,連接上去就很快了搪搏,在linux沒有這個問題
1.1 初始化連接
private Connection connection;
private HTable table;
HBaseAdmin admin;
@Before
public void init() throws IOException {
Configuration configuration = HBaseConfiguration.create();
//設(shè)置zookeeper的地址狭握,可以有多個,以逗號分隔
configuration.set("hbase.zookeeper.quorum","server1,server2");
//設(shè)置zookeeper的端口
configuration.set("hbase.zookeeper.property.clientPort","2181");
//創(chuàng)建hbase的連接疯溺,這是一個分布式連接
connection = ConnectionFactory.createConnection(configuration);
//獲取hbase中的表
table = (HTable) connection.getTable(TableName.valueOf("user"));
//這個admin是管理table時使用的论颅,比如說創(chuàng)建表
admin = (HBaseAdmin) connection.getAdmin();
}
1.2 創(chuàng)建表
創(chuàng)建表只需要指定列族名就行了哎垦,不需要指定列名,列名可以動態(tài)添加
/**
* 創(chuàng)建表恃疯,創(chuàng)建表只需要指定列族漏设,不需要指定列
* 其實用命令真的會更快,create 'user','info1','info2'
*/
@Test
public void createTable() throws IOException {
//聲明一個表名
TableName tableName = TableName.valueOf("user");
//構(gòu)造一個表的描述
HTableDescriptor desc = new HTableDescriptor(tableName);
//創(chuàng)建列族
HColumnDescriptor family1 = new HColumnDescriptor("info1");
HColumnDescriptor family2 = new HColumnDescriptor("info2");
//添加列族
desc.addFamily(family1);
desc.addFamily(family2);
//創(chuàng)建表
admin.createTable(desc);
}
1.3 插入數(shù)據(jù)(修改數(shù)據(jù))
這里之所以還帶了修改數(shù)據(jù)今妄,是因為對于同一個key的指定列族中的列重新插入數(shù)據(jù)郑口,就是修改數(shù)據(jù)
/**
* 添加數(shù)據(jù)
* 對同一個row key進行重新put同一個cell就是修改數(shù)據(jù)
*/
@Test
public void insertUpdate() throws IOException {
//構(gòu)造參數(shù)是row key,必傳
Put put = new Put(Bytes.toBytes("zhangsan_123" + i));
//put.add()已經(jīng)被棄用了
//這里的參數(shù)依次為盾鳞,列族名犬性,列名,值
put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("name"),Bytes.toBytes("lisi" + i));
put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("age"),Bytes.toBytes(22 + i));
put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("sex"),Bytes.toBytes("男"));
put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("address"),Bytes.toBytes("天堂" + i));
table.put(put);
//table.put(List<Put>); //通過一個List集合腾仅,可以添加一個集合
}
1.4 刪除數(shù)據(jù)
刪除數(shù)據(jù)使用Delete對象就行了乒裆,我們可以刪除一行,或者刪除一個列族推励,或者刪除一個列族中的指定列
/**
* 刪除數(shù)據(jù)
*/
@Test
public void delete() throws IOException {
Delete deleteRow = new Delete(Bytes.toBytes("zhangsan_1235")); //刪除一個行
Delete delete = new Delete(Bytes.toBytes("zhangsan_1235"));
delete.addFamily(Bytes.toBytes("info1"));//刪除該行的指定列族
delete.addColumn(Bytes.toBytes("info1"),Bytes.toBytes("name"));//刪除指定的一個單元
table.delete(deleteRow);
//table.delete(List<Delete>); //通過添加一個list集合鹤耍,可以刪除多個
}
1.5 查詢單條數(shù)據(jù)
/**
* 查詢單條數(shù)據(jù)
* @throws IOException
*/
@Test
public void queryByKey() throws IOException {
String rowKey = "zhangsan_1235";
Get get = new Get(Bytes.toBytes(rowKey));
Result result = table.get(get);
byte[] address = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("address")); //讀取單條記錄
byte[] name = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("name")); //讀取單條記錄
byte[] sex = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("sex")); //讀取單條記錄
byte[] age = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("age")); //讀取單條記錄
System.out.print(Bytes.toString(name) + ",");
System.out.print(Bytes.toString(sex) + ",");
System.out.print(Bytes.toString(address) + ",");
System.out.print(Bytes.toInt(age) + ",");
System.out.println();
}
1.6 全表掃描
完整的全表掃描要慎用,不過全表掃描中可以指定很多過濾器验辞,我們可以很好的使用它
/**
* 全表掃描
*/
@Test
public void scanData() throws IOException {
Scan scan = new Scan();
ResultScanner resultScanner = table.getScanner(scan);
printResult(resultScanner);
}
我們看到上方掃描后得到一個ResultScanner
惰蜜,這是一個掃描結(jié)果,我們來看一下要如何使用它
private void printResult(ResultScanner resultScanner) {
for (Result result : resultScanner) {
byte[] address = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("address")); //讀取單條記錄
byte[] name = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("name")); //讀取單條記錄
byte[] sex = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("sex")); //讀取單條記錄
byte[] age = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("age")); //讀取單條記錄
byte[] rowKey = result.getRow(); //獲取rowKey
System.out.print(Bytes.toString(rowKey) + ",");
System.out.print(Bytes.toString(name) + ",");
System.out.print(Bytes.toString(sex) + ",");
System.out.print(Bytes.toString(address) + ",");
System.out.print((age == null ? null : Bytes.toInt(age)) + ",");
System.out.println();
}
}
我們已經(jīng)把這個打印結(jié)果的代碼封裝成為了一個方法了受神,我們接下來很多地方都會用到它
1.7 區(qū)間掃描
區(qū)間掃描我們用處也很多,因為row key是按照字典序來排列的格侯,我們可以根據(jù)這個特性鼻听,查找某一個用戶指定時間段的數(shù)據(jù),比如查詢用戶A昨天到今天的數(shù)據(jù)
在查詢的過程中联四,我們還可以指定返回的結(jié)果撑碴,比如指定返回一個列族,以及返回指定的列朝墩,這樣可以增加查詢速度
/**
* 區(qū)間掃描
*/
@Test
public void areaScanData() throws IOException {
Scan scan = new Scan();
scan.withStartRow(Bytes.toBytes("zhangsan_1232")); //設(shè)置開始行
scan.withStopRow(Bytes.toBytes("zhangsan_12352")); //設(shè)置結(jié)束行
//scan.addColumn(Bytes.toBytes("info1"),Bytes.toBytes("name"));//查詢指定列
scan.addFamily(Bytes.toBytes("info1"));//查詢指定列族
ResultScanner resultScanner = table.getScanner(scan);
printResult(resultScanner);
}
1.8 列值過濾器
列值過濾器也是我們很常用的操作醉拓,它提供了一種甚至值的條件查詢。類似sql中的where field = 'xxx'
收苏,這極大的擴展了hbase的查詢方式亿卤,因為如果只是根據(jù)row key來查詢,那么很多產(chǎn)景都不適用hbase
/**
* 全表掃描時加過濾器 --> 列值過濾器
*/
@Test
public void scanDataByFilter1() throws IOException {
Scan scan = new Scan();
/*
* 第一個參數(shù): 列族
* 第二個參數(shù): 列名
* 第三個參數(shù): 是一個枚舉類型
* CompareOp.EQUAL 等于
* CompareOp.LESS 小于
* CompareOp.LESS_OR_EQUAL 小于或等于
* CompareOp.NOT_EQUAL 不等于
* CompareOp.GREATER_OR_EQUAL 大于或等于
* CompareOp.GREATER 大于
*/
SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(Bytes.toBytes("info1"), Bytes.toBytes("name"), CompareFilter.CompareOp.GREATER_OR_EQUAL, Bytes.toBytes("zhangsan8"));
//這個方法很重要鹿霸,需要注意排吴,當此過濾器過濾時,如果遇到該列值為NULL的情況懦鼠,如果設(shè)置的參數(shù)為true钻哩,則會過濾掉這一行屹堰,如果設(shè)置的參數(shù)為false,那么則會把這一行的結(jié)果返回街氢,默認為false
singleColumnValueFilter.setFilterIfMissing(true);
scan.setFilter(singleColumnValueFilter);
ResultScanner resultScanner = table.getScanner(scan);
printResult(resultScanner);
}
由上面的代碼可以看到扯键,我們使用的的是SingleColumnValueFilter
,它傳入了四個參數(shù)珊肃,我們分別介紹一下
- 第一個:列族
- 第二個:列名
- 第三個:是一個枚舉類型荣刑,告訴列值匹配條件,類型如下
-
CompareOp.EQUAL
等于 -
CompareOp.LESS
小于 -
CompareOp.LESS_OR_EQUAL
小于或等于 -
CompareOp.NOT_EQUAL
不等于 -
CompareOp.GREATER_OR_EQUAL
大于或等于 -
CompareOp.GREATER
大于
-
- 第四個:是匹配的值
1.9 前綴過濾器
前綴過濾器和表里面的內(nèi)容沒有關(guān)系近范,它只是用來匹配指定的列的嘶摊,,比如有這樣兩個列 name1 和name2 评矩,通過這個過濾器叶堆,就會查詢這兩個列的所有數(shù)據(jù),當然斥杜,其實這個方式和scan.addColumn
差不多虱颗,并且它會匹配到多個列族
/**
* 全表掃描時加過濾器 --> 前綴過濾器
*
* 這里的查詢和單元內(nèi)容沒有關(guān)系,僅僅是匹配列名蔗喂,比如有這樣兩個列 name1 和name2 忘渔,通過這個過濾器,就會查詢這兩個列的所有數(shù)據(jù)缰儿,當然畦粮,其實這個方式和scan.addColumn差不多,
* 且并會匹配到其它列族的列名
*
*/
@Test
public void scanDataByFilter2() throws IOException {
ColumnPrefixFilter columnPrefixFilter = new ColumnPrefixFilter(Bytes.toBytes("name"));
Scan scan = new Scan();
scan.setFilter(columnPrefixFilter);
ResultScanner resultScanner = table.getScanner(scan);
printResult(resultScanner);
}
1.10 多列值前綴過濾器
好吧乖阵,這個過濾器和上面的前綴過濾器ColumnPrefixFilter
差不多宣赔,區(qū)別就是可以匹配多個,其實作用并不大
/**
* 全表掃描時添加過濾器 --> 多個列值前綴過濾器
*
* 這個過濾器和 ColumnPrefixFilter 過濾器差不多瞪浸,但是能匹配多個前綴
*/
@Test
public void scanDataFilter3() throws IOException {
Scan scan = new Scan();
byte[][] prefix = new byte[][]{Bytes.toBytes("name"),Bytes.toBytes("add")};
MultipleColumnPrefixFilter multipleColumnPrefixFilter = new MultipleColumnPrefixFilter(prefix);
scan.setFilter(multipleColumnPrefixFilter);
ResultScanner resultScanner = table.getScanner(scan);
printResult(resultScanner);
}
1.11 按row key正則表達式查找
這個也是hbase查找中經(jīng)常用到的功能
/**
* row key查找
*/
@Test
public void scanByRowKey() throws IOException {
//查找以指定內(nèi)容開頭的
Filter rowKeyFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator("^zhangsan_1239"));
Scan scan = new Scan();
scan.setFilter(rowKeyFilter);
ResultScanner resultScanner = table.getScanner(scan);
printResult(resultScanner);
}
1.12 同時使用多個過濾器
我們可能有這樣的情況儒将,我們想掃描指定row key范圍的,又想指定address為深圳的对蒲,或者是更多的一些條件钩蚊,我們要怎么做呢?我們需要組合一些過濾器
/**
* 使用多個過濾器蹈矮,并合查找砰逻,可以同時設(shè)置多個過濾器
*
* 這里查找row key中,name列等于指定值的
*/
@Test
public void scanByFilterList() throws IOException {
/*
* 我們需要注意Operator這個參數(shù)泛鸟,這是一個枚舉類型诱渤,里面有兩個類型
* Operator.MUST_PASS_ALL 需要通過全部的條件,也就是并且谈况,and &&
* Operator.MUST_PASS_ONE 任何一個條件滿足都可以勺美,也就是或者递胧,or ||
*/
FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);
//row key正則表達式的過濾器
Filter rowKeyFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator("^zhangsan_1239"));
//列值過濾器
SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(Bytes.toBytes("info1"), Bytes.toBytes("name"), CompareFilter.CompareOp.EQUAL, Bytes.toBytes("zhangsan9"));
//把兩個filter添加進filterList中
filterList.addFilter(rowKeyFilter);
filterList.addFilter(singleColumnValueFilter);
Scan scan = new Scan();
scan.setFilter(filterList);
ResultScanner resultScanner = table.getScanner(scan);
printResult(resultScanner);
}
我們可以通過FilterList
來添加多個過濾器,然后把這個對象設(shè)置進去赡茸,我們看下這個對象的構(gòu)造方法,Operator
也是一個枚舉類型缎脾,類型如下
- Operator.MUST_PASS_ALL 需要通過全部的條件,也就是并且占卧,and &&
- Operator.MUST_PASS_ONE 任何一個條件滿足都可以遗菠,也就是或者,or ||
1.13 完整代碼
之前所有代碼的java類如下
package com.xiaojiezhu.hbase.demo;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
/**
* @author xiaojie.zhu
*/
public class HbaseTest {
private Connection connection;
private HTable table;
HBaseAdmin admin;
@Before
public void init() throws IOException {
Configuration configuration = HBaseConfiguration.create();
//設(shè)置zookeeper的地址华蜒,可以有多個辙纬,以逗號分隔
configuration.set("hbase.zookeeper.quorum","dockerServer");
//設(shè)置zookeeper的端口
configuration.set("hbase.zookeeper.property.clientPort","2181");
//創(chuàng)建hbase的連接,這是一個分布式連接
connection = ConnectionFactory.createConnection(configuration);
//獲取hbase中的表
table = (HTable) connection.getTable(TableName.valueOf("user"));
//這個admin是管理table時使用的叭喜,比如說創(chuàng)建表
admin = (HBaseAdmin) connection.getAdmin();
}
/**
* 創(chuàng)建表贺拣,創(chuàng)建表只需要指定列族,不需要指定列
* 其實用命令真的會更快捂蕴,create 'user','info1','info2'
*/
@Test
public void createTable() throws IOException {
//聲明一個表名
TableName tableName = TableName.valueOf("user");
//構(gòu)造一個表的描述
HTableDescriptor desc = new HTableDescriptor(tableName);
//創(chuàng)建列族
HColumnDescriptor family1 = new HColumnDescriptor("info1");
HColumnDescriptor family2 = new HColumnDescriptor("info2");
//添加列族
desc.addFamily(family1);
desc.addFamily(family2);
//創(chuàng)建表
admin.createTable(desc);
}
/**
* 添加數(shù)據(jù)
* 對同一個row key進行重新put同一個cell就是修改數(shù)據(jù)
*/
@Test
public void insertUpdate() throws IOException {
//構(gòu)造參數(shù)是row key譬涡,必傳
for(int i = 0 ; i < 100 ; i ++){
Put put = new Put(Bytes.toBytes("zhangsan_123" + i));
//put.add()已經(jīng)被棄用了
//這里的參數(shù)依次為,列族名啥辨,列名涡匀,值
put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("name"),Bytes.toBytes("lisi" + i));
put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("age"),Bytes.toBytes(22 + i));
put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("sex"),Bytes.toBytes("男"));
put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("address"),Bytes.toBytes("天堂" + i));
table.put(put);
//table.put(List<Put>); //通過一個List集合,可以添加一個集合
}
}
/**
* 刪除數(shù)據(jù)
*/
@Test
public void delete() throws IOException {
Delete deleteRow = new Delete(Bytes.toBytes("zhangsan_1235")); //刪除一個行
Delete delete = new Delete(Bytes.toBytes("zhangsan_1235"));
delete.addFamily(Bytes.toBytes("info1"));//刪除該行的指定列族
delete.addColumn(Bytes.toBytes("info1"),Bytes.toBytes("name"));//刪除指定的一個單元
table.delete(deleteRow);
//table.delete(List<Delete>); //通過添加一個list集合溉知,可以刪除多個
}
/**
* 查詢單條數(shù)據(jù)
* @throws IOException
*/
@Test
public void queryByKey() throws IOException {
String rowKey = "zhangsan_1235";
Get get = new Get(Bytes.toBytes(rowKey));
Result result = table.get(get);
byte[] address = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("address")); //讀取單條記錄
byte[] name = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("name")); //讀取單條記錄
byte[] sex = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("sex")); //讀取單條記錄
byte[] age = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("age")); //讀取單條記錄
System.out.print(Bytes.toString(name) + ",");
System.out.print(Bytes.toString(sex) + ",");
System.out.print(Bytes.toString(address) + ",");
System.out.print(Bytes.toInt(age) + ",");
System.out.println();
}
/**
* 全表掃描
*/
@Test
public void scanData() throws IOException {
Scan scan = new Scan();
ResultScanner resultScanner = table.getScanner(scan);
printResult(resultScanner);
}
/**
* 區(qū)間掃描
*/
@Test
public void areaScanData() throws IOException {
Scan scan = new Scan();
scan.withStartRow(Bytes.toBytes("zhangsan_1232")); //設(shè)置開始行
scan.withStopRow(Bytes.toBytes("zhangsan_12352")); //設(shè)置結(jié)束行
//scan.addColumn(Bytes.toBytes("info1"),Bytes.toBytes("name"));//查詢指定列
scan.addFamily(Bytes.toBytes("info1"));//查詢指定列族
ResultScanner resultScanner = table.getScanner(scan);
printResult(resultScanner);
}
/**
* 全表掃描時加過濾器 --> 列值過濾器
*/
@Test
public void scanDataByFilter1() throws IOException {
Scan scan = new Scan();
/*
* 第一個參數(shù): 列族
* 第二個參數(shù): 列名
* 第三個參數(shù): 是一個枚舉類型
* CompareOp.EQUAL 等于
* CompareOp.LESS 小于
* CompareOp.LESS_OR_EQUAL 小于或等于
* CompareOp.NOT_EQUAL 不等于
* CompareOp.GREATER_OR_EQUAL 大于或等于
* CompareOp.GREATER 大于
*/
SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(Bytes.toBytes("info1"), Bytes.toBytes("name"), CompareFilter.CompareOp.GREATER_OR_EQUAL, Bytes.toBytes("zhangsan8"));
scan.setFilter(singleColumnValueFilter);
ResultScanner resultScanner = table.getScanner(scan);
printResult(resultScanner);
}
/**
* 全表掃描時加過濾器 --> 前綴過濾器
*
* 這里的查詢和單元內(nèi)容沒有關(guān)系陨瘩,僅僅是匹配列名,比如有這樣兩個列 name1 和name2 级乍,通過這個過濾器舌劳,就會查詢這兩個列的所有數(shù)據(jù),當然卡者,其實這個方式和scan.addColumn差不多,
* 且并會匹配到其它列族的列名
*
*/
@Test
public void scanDataByFilter2() throws IOException {
ColumnPrefixFilter columnPrefixFilter = new ColumnPrefixFilter(Bytes.toBytes("name"));
Scan scan = new Scan();
scan.setFilter(columnPrefixFilter);
ResultScanner resultScanner = table.getScanner(scan);
printResult(resultScanner);
}
/**
* 全表掃描時添加過濾器 --> 多個列值前綴過濾器
*
* 這個過濾器和 ColumnPrefixFilter 過濾器差不多客们,但是能匹配多個前綴
*/
@Test
public void scanDataFilter3() throws IOException {
Scan scan = new Scan();
byte[][] prefix = new byte[][]{Bytes.toBytes("name"),Bytes.toBytes("add")};
MultipleColumnPrefixFilter multipleColumnPrefixFilter = new MultipleColumnPrefixFilter(prefix);
scan.setFilter(multipleColumnPrefixFilter);
ResultScanner resultScanner = table.getScanner(scan);
printResult(resultScanner);
}
/**
* row key查找
*/
@Test
public void scanByRowKey() throws IOException {
//查找以指定內(nèi)容開頭的
Filter rowKeyFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator("^zhangsan_1239"));
Scan scan = new Scan();
scan.setFilter(rowKeyFilter);
ResultScanner resultScanner = table.getScanner(scan);
printResult(resultScanner);
}
/**
* 使用多個過濾器崇决,并合查找,可以同時設(shè)置多個過濾器
*
* 這里查找row key中底挫,name列等于指定值的
*/
@Test
public void scanByFilterList() throws IOException {
/*
* 我們需要注意Operator這個參數(shù)恒傻,這是一個枚舉類型,里面有兩個類型
* Operator.MUST_PASS_ALL 需要通過全部的條件建邓,也就是并且盈厘,and &&
* Operator.MUST_PASS_ONE 任何一個條件滿足都可以,也就是或者官边,or ||
*/
FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);
//row key正則表達式的過濾器
Filter rowKeyFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator("^zhangsan_1239"));
//列值過濾器
SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(Bytes.toBytes("info1"), Bytes.toBytes("name"), CompareFilter.CompareOp.EQUAL, Bytes.toBytes("zhangsan9"));
//把兩個filter添加進filterList中
filterList.addFilter(rowKeyFilter);
filterList.addFilter(singleColumnValueFilter);
Scan scan = new Scan();
scan.setFilter(filterList);
ResultScanner resultScanner = table.getScanner(scan);
printResult(resultScanner);
}
private void printResult(ResultScanner resultScanner) {
for (Result result : resultScanner) {
byte[] address = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("address")); //讀取單條記錄
byte[] name = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("name")); //讀取單條記錄
byte[] sex = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("sex")); //讀取單條記錄
byte[] age = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("age")); //讀取單條記錄
byte[] rowKey = result.getRow(); //獲取rowKey
System.out.print(Bytes.toString(rowKey) + ",");
System.out.print(Bytes.toString(name) + ",");
System.out.print(Bytes.toString(sex) + ",");
System.out.print(Bytes.toString(address) + ",");
System.out.print((age == null ? null : Bytes.toInt(age)) + ",");
System.out.println();
}
}
}