hbase-sdk是基于hbase-client和hbase-thrift的原生API封装的一款轻量级的HBase ORM框架。 针对HBase各版本API(1.x~2.x)间的差异,在其上剥离出了一层统一的抽象。并提供了以类SQL的方式来读写HBase表中的数据。
在普通的java项目和Spring Boot项目中,你可以分别依赖hbase-sdk-template_${hbase.adapter.version}和spring-boot-starter-hbase_${hbase.adapter.version}两个模块来使用hbase-sdk封装的统一API。其中hbase.adapter.version暂只支持:1.2、1.4、2.2
🐾 快速开始 | 🎬 视频教程 | 🌚 官方文档 | 💰 捐赠我们 | 🌾 English
hbase-sdk
基于HBase的原生API,封装了对HBase表及其数据的DML和DDL操作,同时,也是一款轻量级的ORM框架,提供了数据模型类绑定HBase表实体的能力,与原生的API相比,其优势如下:
- 基于原生API中Get/Put/Scan等功能,重新定义了统一的操作接口,屏蔽了底层HBase各版本原生API间的差异,在面对集群跨大版本升级时,业务伙伴只需对应升级自己项目中的
hbase-client
的版本即可。 - 简化了原生API较为复杂的调用方式,在ORM特性的加持之下,没有HBase API调用经验的开发伙伴,也能快速完成对HBase表数据的读写业务。
- 对HBase的原生thrift api进行了池化封装,类似于Jedis-pool,增强了thrift api生产环境中使用的稳定性。
- 使用spring-boot-starter-hbase可快速与SpringBoot无缝集成。
- 提供了类SQL的方式——HQL来读写HBase表中的数据,进一步简化了原生API的使用方式。
API文档地址: https://weixiaotome.gitee.io/hbase-sdk/ 如果觉得这个项目不错可以 star 支持或者 捐赠 它 😊
- 定义了统一的接口规范,消除了HBase不同版本原生API之间的差异
- ORM特性,以注解方式快速实现表、列簇、字段模型与java实体类进行绑定
- 对HBase的原生thrift API进行池化封装,提供了HBaseThriftPool的功能
- HQL,以类SQL的形式读写HBase的表中数据
- 利用spring-boot-starter-hbase无缝与SpringBoot集成
- HBatis,类似于myBatis,提供配置文件管理HQL的功能(规划中)
- 客户端熔断,提供客户端API级别的主备集群切换,保障请求HBase接口服务的高可用(规划中)
- thrift 连接池中连接数的动态扩所容能力(规划中)
https://github.com/CCweixiao/hbase-sdk
https://gitee.com/weixiaotome/hbase-sdk
两边仓库地址是同步更新的,欢迎各位大佬点个star
克隆项目到本地,导入到IDEA中,首次加载项目,会从远程仓库拉取项目所需的依赖,还请耐心等待。
cd hbase-sdk
mvn clean install -Phbase-1.2 # hbase-client:1.2.x
mvn clean install -Phbase-1.4 # hbase-client:1.4.x
mvn clean install -Phbase-2.2 # hbase-client:2.x.x
或者
mvn clean package -Dmaven.test.skip=true -Dfindbugs.skip -Dcheckstyle.skip -Dmaven.javadoc.skip -Phbase-1.2
mvn clean package -Dmaven.test.skip=true -Dfindbugs.skip -Dcheckstyle.skip -Dmaven.javadoc.skip -Phbase-1.4
mvn clean package -Dmaven.test.skip=true -Dfindbugs.skip -Dcheckstyle.skip -Dmaven.javadoc.skip -Phbase-2.2
hbase-sdk
的``hbase-sdk-adapter`模块下的各个子模块中已引入了具体的hbase-shaded-client的依赖,如有需要可以自行更改你想使用的hbase-client版本。
项目结构说明,主要介绍核心模块的作用
├── hbase-sdk-adapter # HBase各版本不兼容API的adapter
│ ├── hbase-sdk-adapter-common
│ ├── hbase-sdk-adapter_1.2
│ ├── hbase-sdk-adapter_1.4
│ ├── hbase-sdk-adapter_2.2
│ └── pom.xml
├── hbase-sdk-common # 通用工具或接口
├── hbase-sdk-dsl # HBase SQL
├── hbase-sdk-examples
│ ├── hbase-sdk-example
│ └── spring-boot-starter-hbase-example
├── hbase-sdk-template # hbase操作模版类API
├── hbase-sdk-thrift # HBase thrift API
└── spring-boot-starter-hbase # spring-boot-starter-hbase
hbase-sdk
UML类图:
hbase-sdk
的各个版本完成开发测试之后,都会发布到maven中央仓库之中,只是最新版本的代码有一定的延迟。如果你想在第一时间体验新功能,可以选择克隆Gitee或Github仓库中的源码,在本地编译并运行测试用例。
hbase-sdk
如果你想在本地进行开发,请确保已经安装了Java8和maven3.6+。同时建议在本地部署一个可连通的HBase开发环境。建议使用docker来快速搭建一个HBase的单机环境,可以参考博客:https://blog.csdn.net/feinifi/article/details/121174846
hbase-sdk
开发所用的工具为IDEA,所以也极力推荐导入项目到IDEA中。
Maven
配置:
创建一个基础的 Maven
工程,HBase SDK 已适配了hbase-client的1.2/1.4/2.x版本API。
如果你的HBase版本是1.2.x,可以使用如下依赖。
<dependency>
<groupId>com.hydraql</groupId>
<artifactId>hbase-sdk-template_1.2</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
如果你的HBase版本是1.4.x,则可以使用如下依赖。
<dependency>
<groupId>com.hydraql</groupId>
<artifactId>hbase-sdk-template_1.4</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
如果你的HBase版本是2.x.x,则可以使用如下依赖。
<dependency>
<groupId>com.hydraql</groupId>
<artifactId>hbase-sdk-template_2.2</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
hbase-sdk
目前最新的版本是3.0.0-SNAPSHOT
。你可以在maven中央仓库中搜索CCweixiao来获取hbase-sdk相关jar包的最新版本。
https://mvnrepository.com/artifact/com.hydraql
或者在git仓库中查看最新的release版本。
当然,如果你想重新编译,扩展你需要的功能,也可以选择下载源码,修改项目根pom.xml文件中的hbase.version
,按照编译指南中的编译命令来操作。
Maven
配置:
创建一个基于Maven
的spring boot工程,在项目pom.xml中加入spring-boot-starter-hbase的依赖。
如果你的HBase版本是1.2.x,可以使用如下依赖。
<dependency>
<groupId>com.hydraql</groupId>
<artifactId>spring-boot-starter-hbase_1.2</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
如果你的HBase版本是1.4.x,可以使用如下依赖。
<dependency>
<groupId>com.hydraql</groupId>
<artifactId>spring-boot-starter-hbase_1.4</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
如果你的HBase版本是2.x.x,可以使用如下依赖。
<dependency>
<groupId>com.hydraql</groupId>
<artifactId>spring-boot-starter-hbase_2.2</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
hbase-sdk
没有把hbase-client的依赖打到自己的包中,所以,除了引入hbase-sdk
的相关依赖之外,你还需要引入hbase-client
的依赖,hbase-client
的版本目前支持1.2.x
、1.4.x
、2.2.x
,请按需引入。(建议使用hbase-shaded-client)。
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-shaded-client</artifactId>
<version>1.2.0</version>
</dependency>
or
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-shaded-client</artifactId>
<version>1.4.3</version>
</dependency>
or
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-shaded-client</artifactId>
<version>2.2.6</version>
</dependency>
普通Java项目
普通认证
// 普通认证
Properties properties = new Properties();
properties.setProperty("hbase.zookeeper.quorum", "myhbase");
properties.setProperty("hbase.zookeeper.property.clientPort", "2181");
// 请按需引入一些额外所需的客户端配置
properties.put("hbase.client.retries.number", "3");
Kerberos认证
Properties properties = new Properties();
properties.put("hbase.zookeeper.quorum", "zk_host1,zk_host1,zk_host1");
properties.put("hbase.zookeeper.property.clientPort", "2181");
properties.put("hbase.security.authentication", "kerberos");
properties.put("kerberos.principal", "hbase@HADOOP.LEO.COM");
properties.put("keytab.file", "/etc/hbase/conf/hbase.keytab");
properties.put("hbase.regionserver.kerberos.principal", "hbase/_HOST@HADOOP.LEO.COM");
properties.put("hbase.master.kerberos.principal", "hbase/_HOST@HADOOP.LEO.COM");
// 指定kdc服务相关的配置方式有如下两种:
// 方式一:指定krb5.conf路径
properties.put("java.security.krb5.conf", "/etc/krb5.conf");
// 方式二:指定java.security.krb5.realm和java.security.krb5.kdc
properties.put("java.security.krb5.realm", "HADOOP.LEO.COM");
properties.put("java.security.krb5.kdc", "你自己的kdc服务地址");
// 一些额外的客户端参数
properties.put("hbase.client.retries.number", "3");
IHBaseAdminTemplate adminTemplate adminTemplate = new HBaseAdminTemplateImpl.Builder().properties(properties).build();
System.out.println(adminTemplate.listTableNames());
Spring Boot项目
普通认证
application.yaml
spring:
datasource:
hbase:
zk-host-list: zk_host1,zk_host2,zk_host3
zk-client-port: 2181 # (可选,默认2181)
dfs-root-dir: /hbase # (可选,默认/hbase)
zk-node-parent: /hbase # (可选,默认/hbase)
security-auth-way: simple # (可选,默认simple)
client-properties: hbase.client.retries.number=3;key1=value2
server:
port: 8088
Kerberos认证
spring:
datasource:
hbase:
zk-host-list: zk_host1,zk_host2,zk_host3
zk-client-port: 2181 # (可选,默认2181)
dfs-root-dir: /hbase # (可选,默认/hbase)
zk-node-parent: /hbase # (可选,默认/hbase)
security-auth-way: kerberos
kerberos-principal: hbase@HADOOP.LEO.COM
keytab-file-path: /etc/hbase/conf/hbase.keytab
rs-kerberos-principal: hbase/_HOST@HADOOP.LEO.COM
master-kerberos-principal: hbase/_HOST@HADOOP.LEO.COM
# krb5-conf-path和krb5-realm、krb5-kdc-server-addr任选一种配置KDC的方式
krb5-conf-path: /etc/krb5.conf
krb5-realm:
krb5-kdc-server-addr:
client-properties: hbase.client.retries.number=3;key1=value2
server:
port: 8088
普通项目
// 数据读写API的操作模版类
IHBaseTableTemplate tableTemplate = new HBaseAdminTemplateImpl.Builder()
.properties(properties).build();
// Admin操作模版类
IHBaseAdminTemplate adminTemplate = new HBaseAdminTemplateImpl.Builder()
.properties(properties).build();
// HQL操作模版类
IHBaseSqlTemplate sqlTemplate = new HBaseSqlTemplateImpl.Builder()
.properties(properties).build()
SpringBoot项目
@Autowired 依赖注入
@Service
public class UserService {
@Autowired
private IHBaseTableTemplate tableTemplate;
@Autowired
private IHBaseAdminTemplate adminTemplate;
@Autowired
private IHBaseSqlTemplate sqlTemplate;
}
HBaseAdminTemplate封装了HBaseAdmin的常用操作,比如namespace的管理、表的管理、以及快照管理等等,后续这些API将会更加完整。
@Test
public void createNameSpace() {
NamespaceDesc namespaceDesc = new NamespaceDesc();
namespaceDesc.setNamespaceName("test_nn");
namespaceDesc.addNamespaceProp("createdBy", "leojie");
adminTemplate.createNamespaceAsync(namespaceDesc);
}
@Test
@Test
public void testCreateTable() {
ColumnFamilyDesc f1 = new ColumnFamilyDesc.Builder()
.familyName("f1")
.build();
ColumnFamilyDesc f2 = new ColumnFamilyDesc.Builder()
.familyName("f2")
.timeToLive(3600)
.versions(3)
.build();
HTableDesc tableDesc = new HTableDesc.Builder()
.defaultTableDesc("test_nn:test_table")
.maxFileSize(51400000L)
.addTableProp("hbase.hstore.block.storage.policy", "HOT")
.addColumnFamilyDesc(f1)
.addColumnFamilyDesc(f2)
.build();
adminTemplate.createTable(tableDesc);
}
可以参考相关API文档或hbase-template
模块下的测试用例
类似于Hibernate,你也可以使用hbase-sdk框架所提供的ORM特性,来实现对HBase的数据读写操作。
public class CityTag {
private String tagName;
public CityTag(String tagName) {
this.tagName = tagName;
}
// 省略Getter/Setter/toString
}
@HBaseTable(namespaceName = "default", tableName = "t2", defaultFamilyName = "info")
public class CityModel {
@HBaseRowKey
private String cityId;
private String cityName;
private String cityAddress;
@HBaseColumn(familyName = "detail")
private Integer cityArea;
@HBaseColumn(familyName = "detail", toUpperCase = true)
private Integer totalPopulation;
@HBaseColumn(familyName = "detail", columnName = "cityTagList")
private List<CityTag> cityTagList;
// 省略Getter/Setter/toString
}
@HBaseTable
注解用于定义HBase的表信息
@HBaseTable(namespaceName = "default", tableName = "t2", defaultFamilyName = "info")
1)namespaceName:用于指定该表的命名空间,默认:default
2)tableName:用于指定该表的表名,如果不指定,表名则为类名的组合单词拆分转小写加'_'拼接,如:CityModel对应的表名为:city_model。 3)defaultFamilyName:用于定义如果有字段不特配置(@HBaseRowKey注解中的familyName)列簇名,则使用此处配置的列簇名。
@HBaseRowKey
注解用于定义某一个属性字段是用作存储rowKey数据的,是必须要设置的,如:
@HBaseRowKey
private String cityId;
该注解表示cityId字段为rowKey。
@HBaseColumn
注解用于定义HBase的列簇和列名信息,如:
@HBaseColumn(familyName = "detail", columnName = "TOTAL_POPULATION", toUpperCase = true)
private Integer totalPopulation;
1)familyName:指定列簇名,不指定,则使用defaultFamilyName配置的列簇名。
2)columnName:指定列名,不指定则默认使用字段名的组合单词拆分转小写加'_'拼接,如:isVip,对应的字段名是:is_vip
3)toUpperCase:定义字段名是否转大写,如:isVip -> IS_VIP,默认值:false,不做转换。
@Test
public void testSaveUser() {
CityModel cityModel = new CityModel();
cityModel.setCityId("10001");
cityModel.setCityName("上海");
cityModel.setCityAddress("上海市");
cityModel.setCityArea(10000);
cityModel.setTotalPopulation(200000); cityModel.setCityTagList(tagNameList.stream().map(CityTag::new).collect(Collectors.toList()));
CityModel city = tableTemplate.save(cityModel);
}
除此之外,保存数据时也可以不必构造数据模型类,而选择直接构造map数据模型。
@Test
public void testToSave() {
Map<String, Object> data = new HashMap<>();
data.put("info1:addresses", Arrays.asList("广州", "深圳"));
data.put("info1:username", "leo");
data.put("info1:age", 18);
data.put("INFO2:IS_VIP", true);
data.put("info1:pay", 10000.1d);
data.put("info1:create_by", "tom");
data.put("info1:create_time", System.currentTimeMillis());
Map<String, Object> contactInfo = new HashMap<>(2);
contactInfo.put("email", "2326130720@qq.com");
contactInfo.put("phone", "18739577988");
contactInfo.put("address", "浦东新区");
data.put("info1:contact_info", contactInfo);
hBaseTemplate.save("TEST:LEO_USER", "10002", data);
System.out.println("用户数据保存成功!");
}
@Test
public void testToSaveBatch() {
Map<String, Map<String, Object>> data = new HashMap<>();
Map<String, Object> data1 = new HashMap<>();
data1.put("info1:username", "kangkang");
data1.put("info1:age", 18);
data1.put("INFO2:IS_VIP", true);
Map<String, Object> data2 = new HashMap<>();
data2.put("info1:username", "jane");
data2.put("info1:age", 18);
data2.put("INFO2:IS_VIP", false);
data.put("12003", data1);
data.put("11004", data2);
hBaseTemplate.saveBatch("TEST:LEO_USER", data);
System.out.println("用户数据批量保存成功!");
}
@Test
public void testGetJavaBean() {
Optional<CityModel> a10001 = tableTemplate.getRow("a10001", CityModel.class);
Optional<CityModel> a10001F = tableTemplate.getRow("a10001", "info", CityModel.class);
System.out.println(a10001.orElse(new CityModel()));
System.out.println(a10001F);
}
查询数据返回Map
@Test
public void testGetRowToMap() {
Map<String, String> d1 = tableTemplate.getRowToMap("t1", "1001", true);
JSONArray objects = JSON.parseArray(d1.get("f3:tags"));
Map<String, String> d2 = tableTemplate.getRowToMap("t1", "1002", false);
List<String> rows = new ArrayList<>(2);
rows.add("1001");
rows.add("1002");
Map<String, Map<String, String>> d3 = tableTemplate.getRowsToMap("t1", rows, true);
System.out.println(d1);
System.out.println(d2);
System.out.println(d3);
}
普通scan查询
@Test
public void testScanWithLimit() {
ScanQueryParamsBuilder scanQueryParamsBuilder = new ScanQueryParamsBuilder.Builder()
.familyName("info")
.columnNames(Arrays.asList("city_name", "city_address", "cityTagList"))
.limit(2)
.build();
List<CityModel> cityModels = tableTemplate.scan(scanQueryParamsBuilder, CityModel.class);
System.out.println(cityModels);
}
根据起止row查询数据
@Test
public void testScanWithStartAndEndRow() {
// 不包含endRow的数据
ScanQueryParamsBuilder scanQueryParamsBuilder = new ScanQueryParamsBuilder.Builder()
.startRow("a10001")
.stopRow("a10002")
.build();
List<CityModel> cityModels = tableTemplate.scan(scanQueryParamsBuilder, CityModel.class);
System.out.println(cityModels);
}
指定过滤器的scan查询
@Test
public void testScanWithFilter() {
ScanQueryParamsBuilder scanQueryParamsBuilder = new ScanQueryParamsBuilder.Builder()
.filter(new IHBaseFilter<Filter>() {
@Override
public Filter customFilter() {
List<Filter> filters = new ArrayList<>(2);
// 筛选row key 大于b20001的数据
Filter rowFilter = new RowFilter(CompareFilter.CompareOp.GREATER,
new BinaryComparator("b20001".getBytes()));
// 筛选列前缀city_address的数据
ColumnPrefixFilter columnPrefixFilter = new ColumnPrefixFilter(Bytes.toBytes("city_address"));
// 筛选列值与深圳市相等的数据
ValueFilter valueFilter = new ValueFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes("深圳市")));
// 多过滤器,注意顺序
filters.add(rowFilter);
filters.add(columnPrefixFilter);
filters.add(valueFilter);
// 需所有条件全部通过
FilterList andFilterList = new FilterList(FilterList.Operator.MUST_PASS_ALL, filters);
// 满足其中一个条件即可
FilterList orFilterList = new FilterList(FilterList.Operator.MUST_PASS_ONE, filters);
return orFilterList;
}
})
.build();
List<CityModel> cityModels = tableTemplate.scan(scanQueryParamsBuilder, CityModel.class);
System.out.println(cityModels);
}
@Test
public void testDeleteData() {
hBaseTemplate.delete("TEST:LEO_USER", "12003");
hBaseTemplate.delete("TEST:LEO_USER", "11004", "INFO2");
hBaseTemplate.delete("TEST:LEO_USER", "10001", "info1", "addresses");
System.out.println("数据删除完成");
}
批量删除数据
@Test
public void testDeleteBatch() {
hBaseTemplate.deleteBatch("TEST:LEO_USER", Arrays.asList("10001", "10002"));
hBaseTemplate.deleteBatch("TEST:LEO_USER", Collections.singletonList("10003"), "INFO2");
hBaseTemplate.deleteBatch("TEST:LEO_USER", Collections.singletonList("10004"),
"info1", "age", "username");
}
hbase-sdk
从2.0.6版本开始,开始提供HQL功能,并在3.0.0版本中得到极大优化,一种以类SQL的方式读写HBase集群的数据,进一步降低了普通API的使用复杂度。HQL的操作依赖HBaseSqlTemplate
来完成,
因此在使用之前,需先构造好HBaseSqlTemplate
的对象实例。
// 1. 创建HBase SQL操作的模版类HBaseSqlTemplate
private HBaseSqlTemplate hBaseSqlTemplate = new HBaseTableTemplateImpl.Builder()
.properties(getProperties()).build();
// 把HBase的连接配置信息存储在Properties中
Properties getProperties() {
Properties properties = new Properties();
properties.setProperty("hbase.zookeeper.quorum", "myhbase");
properties.setProperty("hbase.zookeeper.property.clientPort", "2181");
return properties;
}
// 2. 创建HBaseTableSchema
HBaseTableSchema tableSchema = new HBaseTableSchema.Builder("test:test_sql")
.addColumn("f1", "id")
.addColumn("f1", "name")
.addColumn("f1", "age", ColumnType.IntegerType)
.addColumn("f2", "address")
.addRow("row_key")
.scanBatch(100)
.scanCaching(1000)
.deleteBatch(100)
.scanCacheBlocks(false)
.build();
// 3. 注册HBaseTableSchema至HBaseSqlContext中
HBaseSqlContext.registerTableSchema(tableSchema);
// tableSchema.printSchema();
打印schema
插入一条数据
insert into test:test_sql ( f1:id , f1:name , f1:age , f2:address ) values ( '10001' , 'a_leo' , 15 , 'bj' ) where rowKey = 'a10001'
调用insert保存数据
@Test
public void testInsertSql() {
String hql = "insert into test:test_sql ( f1:id , f1:name , f1:age , f2:address ) values ( '10001' , 'a_leo' , 15 , 'bj' ) where rowKey = 'a10001'";
sqlTemplate.insert(hql);
}
插入数据时还可以指定一些内置的rowkey function
-- 对rowKey进行md5
insert into test:test_sql ( f1:id , f1:name , f1:age , f2:address ) values ( '11111' , 'a_leo' , 15 , 'bj' ) where rowKey = md5 ( 'a1111' )
-- 对rowKey md5取前4位作为前缀用|与原row拼接后形成新的rowKey
insert into test:test_sql ( f1:id , f1:name , f1:age , f2:address ) values ( '11111' , 'a_leo' , 15 , 'bj' ) where rowKey = md5_prefix ( 'a1111' )
row key function 暂时还不支持对参数列表的解析,暂时只能使用function ( 'row key值' )的形式。
查看保存的数据
select * from test:test_sql where rowKey = md5 ( 'a1111' )
select * from test:test_sql where rowKey = md5_prefix ( 'a1111' )
select的调用方法如下:
String hsql = "select * from test:test_sql where rowKey = md5_prefix ( 'a1111' )";
HBaseDataSet dataSet2 = sqlTemplate.select(hsql);
1. 根据rowKey查询数据
select * from test:test_sql where rowKey = 'a10001'
2. 查询一批数据,in row keys
select * from test:test_sql where rowKey in ( 'a10001' , 'a10002' , 'a10003' )
3. 根据startRowKey和endRowKey扫描数据
select * from test:test_sql where ( startKey = 'a10001' , endKey = 'b20006' )
4. 查询不同版本数据
先保存三个版本数据
查询多版本数据
select f1:name from test:test_sql where rowKey = 'row_1000' and maxVersion = 3
5. 判断某一列值
查询时需指定row过滤规则
select * from test:test_sql where ( startKey = 'a10001' , endKey = 'a10006' ) and f1:age <= 18
6. limit
select * from test:test_sql where ( startKey = 'a10001' , endKey = 'a10006' ) and f1:age <= 18 limit 2
7. 查询一批rowkey数据
select * from test:test_sql where rowKey in ( 'a10001' , 'a10002' , 'a10003' )
原始数据
删除某个row key的某个字段
delete f1:age from test:test_sql where rowKey = 'b20004'
指定时间戳删除数据
原始数据如下:
指定时间戳来删除数据,由图示可知,时间戳为1670579504803的数据已被删除
delete f1:age from test:test_sql where rowKey = 'row_10001' and ts = 1670579504803
@Test
public void testDeleteSql(){
String hql = "delete f1:age from test:test_sql where rowKey = 'row_10001'";
sqlTemplate.delete(hql);
}
hbase-client
中的API会直接连接zookeeper,如果客户端对Connection滥用,可能会造成zookeeper的连接耗尽。
hbase-thrift
不仅有跨平台特性,同时也会在底层避免我们直接连接zk。
但如果直接使用hbase thrift的api,你可能会遇到以下几种情况:
- 频繁创建TSocket连接,增加不必要的开销
- 某一时间段内可能频繁创建过多的TSocket,造成本地短连接过多
- 创建完一个TSocket,间隔时间过长不使用,会被服务端主动断开
为了解决上述问题,可以采取对hbase-thrift中的TSocket进行连接池封装。
在hbase-sdk
中HBase Thrift API 连接池的实现基于commons-pool2,类似jedis-pool,代码在hbase-sdk-thrift
模块中。
使用thrift api之前,请先创建HBaseThriftTemplate的对象
HBaseThriftTemplate thriftTemplate = HBaseThriftTemplateFactory.getInstance("localhost", 9090);
HBaseThriftTemplate thriftTemplate = HBaseThriftTemplateFactory.getInstance("localhost", 9090, 10);
HBaseThriftTemplate thriftTemplate = HBaseThriftTemplateFactory.getInstance("localhost", 9090, config);
HBaseThriftTemplate可接受的参数类型:
- thrift server host
- thrift server port
- poolSize 连接池大小,设置后,连接池中核心和最大参数都将是此值
- HBaseThriftPoolConfig config
config为连接池配置类,其默认配置如下,可按需修改:
public class HBaseThriftPoolConfig extends GenericObjectPoolConfig {
public HBaseThriftPoolConfig() {
// 连接池中的最大连接数,默认1,根据服务端可以容纳的最大连接数和当前并发数进行合理设置
setMaxTotal(1);
// 连接池中确保的最少空闲连接数
setMinIdle(1);
// 连接池中允许的最大空闲连接数
setMaxIdle(1);
// 连接池用尽后,调用者是否等待,为true时,maxWaitMillis才生效
setBlockWhenExhausted(true);
// 连接池用尽后,调用者的最大等待时间,毫秒,默认-1,表示永不超时
setMaxWaitMillis(6000);
// 每次从资源池中拿/归还连接是否校验连接的有效性,默认false,避免每次使用或归还连接与服务端进行一次连接开销
setTestOnBorrow(false);
setTestOnReturn(false);
// 开启JMX监控
setJmxEnabled(true);
// 是否开启空闲连接检测,默认false,建议true
setTestWhileIdle(true);
// 空闲连接的检测周期,毫秒,默认-1不进行检测,此处周期设置为3分钟
setTimeBetweenEvictionRunsMillis(60 * 1000);
// 空闲连接检测时,每次检测资源的个数,设置为-1,就是对所有连接进行检测
setNumTestsPerEvictionRun(-1);
// 连接池中连接的最小空闲时间,默认60000毫秒,6分钟
setMinEvictableIdleTimeMillis(60 * 1000);
//硬闲置 3秒没有占用设置为闲置, 检测线程直接剔除闲置,保持的最小空闲数,会被剔除且重新生成 硬闲置设置之后,软闲置设置无效
//setMinEvictableIdleTimeMillis(3000);
//软闲置 3秒没有占用设置为闲置, 当空闲连接>最小空闲数,才执行剔除闲置连接,否则维持最小空闲数,即使闲置了也不会剔除
//setSoftMinEvictableIdleTimeMillis(3000);
}
}
构造数据模型类
@HBaseTable(namespaceName = "test", tableName = "t1", defaultFamilyName = "info")
public class UserModel {
@HBaseRowKey
private String userId;
@HBaseColumn()
private String nickName;
@HBaseColumn(familyName = "detail", columnName = "detailAddress")
private String detailAddress;
@HBaseColumn(familyName = "detail", toUpperCase = true)
private double detailPay;
// 省略getter、setter
}
或者直接保存Map中的数据
Map<String, Object> data = new HashMap<>();
data.put("info:nick_name", "会飞的猪");
data.put("detail:DETAIL_PAY", 1234.5);
data.put("detail:detailAddress", "上海黄浦区");
thriftTemplate.save("test:t1", "u10002", data);
@Test
public void testGetRow() {
Optional<UserModel> userModel = thriftTemplate.getRow("u10001", UserModel.class);
System.out.println(userModel);
Map<String, String> data = thriftTemplate.getRowToMap("test:t1", "u10002", false);
System.out.println(data);
}
查询多条row key
@Test
public void testGetRows() {
thriftTemplate.getRows(Arrays.asList("u10001", "u21000", "u22000"), UserModel.class);
thriftTemplate.getRows(Arrays.asList("u10001", "u21000", "u22000"), "detail", UserModel.class);
thriftTemplate.getRows(Arrays.asList("u10001", "u21000", "u22000"), "detail", Collections.singletonList("detailAddress"), UserModel.class);
}
Map结构数据保存时,会统一把数据转换为字符串类型,所以,当用java实体类绑定时,可能出现报错情况。
全表扫描所有数据,并设置limit
@Test
public void testScanWithLimit() {
ScanQueryParamsBuilder queryParams = new ScanQueryParamsBuilder.Builder()
.limit(2)
.build();
// Map 保存的数据,与模型类保存的数据,非string类型不能互通
List<UserModel> userModelList = thriftTemplate.scan(queryParams, UserModel.class);
System.out.println(userModelList);
}
根据起止row key扫描数据,不包含stopRow
@Test
public void testScanWithStarAndRow() {
ScanQueryParamsBuilder queryParams = new ScanQueryParamsBuilder.Builder()
.startRow("u10001")
.stopRow("u21000")
.build();
List<UserModel> userModelList = thriftTemplate.scan(queryParams, UserModel.class);
System.out.println(userModelList);
}
设置过滤器扫描,列名为nick_前缀,且列对应值ascii码比:不会飞的猪2大的被筛选出
@Test
public void testScanWithFilter() {
// 设置过滤器扫描,列名为nick_前缀,且列对应值ascii码比:不会飞的猪2大的被筛选出
ScanQueryParamsBuilder queryParams = new ScanQueryParamsBuilder.Builder()
.filter(new IHBaseFilter<String>() {
@Override
public String customFilter() {
return "ColumnPrefixFilter('nick_') AND ValueFilter(>=, 'binary:不会飞的猪2')";
}
})
.build();
List<UserModel> userModelList = thriftTemplate.scan(queryParams, UserModel.class);
System.out.println(userModelList);
}
更多API的使用可以参考源码中的测试用例以及相关的API文档。
HQL的语法设计以及antlr4的语法解析,有参考alibaba的开源项目 simplehbase
,在此特别感谢。simplehbase感觉是一个被遗弃的项目,针对的HBase版本是0。94,
已经有超过6年没有维护了。
hbase-sdk
在simplehbase的基础上进行重组和解耦,以兼容hbase-sdk
原有的框架设计,并便于以后的扩展。
HQL的antlr4解析功能不太完善,对语法的要求比较严格,多一个空格少一个空格貌似都会引起语法错误。 后续会针对这些缺点一一优化。
- HBatis,类似于MyBatis的ORM框架,以XML管理SQL的方式维护集群数据的读写操作
- 集成Hystrix熔断框架,实现API层面的主备集群自动切换功能
- Thrift 连接池自动扩所容的能力
- 还有更多
- 对hbase-sdk项目做了大重构,使API抽象程度更高,同时丰富了API的功能,也修复和完善了诸多BUG
- 基于reflectasm重构反射工具类,提升了ORM映射字段的效率
- HQL功能优化
- 工具类优化
- HBase Thrift API上线,以及提供Thrift API 的连接池实现
- HQL功能上线
- 新增功能与代码优化
- 大量重构和优化
- 完善基础API的功能
- 完成ORM特性
- 模块拆分
- ......