HBase轻量级框架Parrot
目录
一、Parrot是什么?
二、Parrot的数据源配置与连接
三、使用无状态的实体Bean执行CRUD;
四、Parrot所支持的原生HBase特性;
五、Parrot中被废弃的事物操作;
六、Parrot的设计实现;
一、Parrot是什么?
在弄清楚Parrot是什么之前,你需要首先弄清楚HBase是什么,HBase能够适用于哪些场景?当这一切你都明白后,再开看看如何使用Parrot。
Parrot是鹦鹉的单词,或许源于家族遗传因素,本人对鸟类一直比较感冒,所以故用此名作为该项目的名字。Parrot是一种超轻量级的HBase框架,它对代码的侵入极低,且不依赖于任何容器,也就是说最简单的Java工程中也能够跑起来。就目前而言,市面上几乎没有成熟的HBase框架,那么Parrot诞生的目的已经很明确了,就是为了开发实惠!
目前开发人员在程序中大部分都是直接使用HBase Client API操作HBase。笔者使用该工具在项目中倒腾了差不多有半年多的时间,不得不说,使用原生的HBase Client操作HBase确实是一件非常痛苦的事情,不仅开发耗时,且代码冗余度极高。所以我在HBase Client API的基础之上,轻量级的封装了一层,以达到极致简化的目的,这便是Parrot的重。Parrot目前的最新版本是1.1,后续版本中,Parrot将会继续迭代。
Parrot的优点如下:
□ 降低原本90%的工作量,使你只需关注于业务;
□ 使用无状态的Entity Bean方式,对HBase进行CRUD操作;
□ 低侵入式设计,与业务耦合极低;
□ 支持从Diamond中进行数据源信息的配置和加载;
□ 数据检索的自动映射操作;
□ 支持HBase原生的TTableInterface接口;
Parrot缺省依赖Diamond、HBase Client API等相关构件!
二、Parrot的数据源配置与连接
Parrot不支持HBase缺省的基于配置文件的方式配置数据源信息,之所以这么做是因为在大规模的项目场景中,资源配置最好是集中式的,以此避免每一次的项目部署都需要修改集群节点中的配置文件,所以Parrot选用了淘宝的Diamond最为集中式配置中心。
Parrot在Diamond中的数据源配置信息(dataId=hbase_info,组名=DEFAULT_GROUP):
rootdir=hdfs://CNSZ141222:30000/hbase
distributed=true
quorum=CNSZ141222,CNSZ141223,CNSZ141224
maxSize=1000
其中rootdir属性配置了HDFS的地址,distributed属性配置了HBase是否集群,quorum属性配置了ZK的与IP地址对应的机器名,最后一个属性maxSize则用于配置TablePool的连接数。
当你第一次使用Parrot的时候,你需要现将上述信息配置在Diamond中。然后在程序中你只需通过如下语句,便可以建立与HBase的连接:
publicclass ParrotTest {
privatestatic ConnectionConfig conn;
@BeforeClass
publicstaticvoid testConnection() {
conn = new ParrotConnectionConfig();
}
}
}
ConnectionConfig接口是Parrot的数据源接口,支持从Diamond集中式配置中心获取HBase的数据源信息并建立与HBase的会话连接。前面我们说过Parrot能够及时响应Diamond的变化,一旦Diamond中的配置信息发生变化,Parrot便会重新调用ConnectionConfig接口中的loadDataSource()方法,重新初始化数据源信息并关闭内部打开的TablePool连接。
ConnectionConfig接口的常用方法如下:
□
MetaDataTablegetTable()
:获取一个用于操作HBase元数据的Table对象.
□
ParrotTablegetTable(java.lang.Class entityClass)
:根据实体Bean中的信息从HBase中获取指定的Table对象;
□ ParrotTemplategetTemplate()
:获取一个用于执行数据检索的ParrotTemplate对象;
□ void
loadDataSource(com.taobao.diamond.manager.ManagerListener listener)
:从Diamond集中式配置中心获取出HBase的数据源信息;
三、使用无状态的实体Bean执行CRUD;
使用无状态的实体Bean操作HBase,翻译白话点,就是使用对象的方式操作HBase。光从名字就能够听出来,既然使用了面向对象特性,那么操作HBase必然比使用原生的HBase Client API更简单,不过Parrot不仅支持实体Bean的方式对HBase执行CRUD操作,并且对于数据检索,Parrot还能够做到将检索的数据结果集,自动映射到实体Bean上。
在使用实体Bean操作HBase之前,实体Bean必须添加@Entity注解,如下所示:
@Entity(tableName = "my_table", cloumnFamily = { "info" })
上述住接种,tableName属性指定了HBase的表名,cloumnFamily则指定了列族,很遗憾的是目前Parrot1.0的版本中,只能够暂时支持一个列族。不过这并不我们的使用,且按常理说,列族的数量越少,HBase的执行效率越快,这也是实时。
定义实体Bean对象:
@Entity(tableName = "my_table", cloumnFamily = { "info" })
publicclass Bean1 {
private String userName, passWord;
public String getUserName() {
returnuserName;
}
publicvoid setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
returnpassWord;
}
publicvoid setPassWord(String passWord) {
this.passWord = passWord;
}
}
使用Parrot操作实体Bean对象执行CRUD操作:
privatestatic ConnectionConfig conn;
@BeforeClass
publicstaticvoid testConnection() {
conn = new ParrotConnectionConfig();
}
@Test
publicvoid testInsert() throws Exception {
ParrotTable table = conn.getTable(Bean1.class);
Bean1 entityObj = new Bean1();
entityObj.setUserName("JohnGao");
entityObj.setPassWord("123456");
table.insert(entityObj, "key1");
}
@Test
publicvoid testInserts() throws Exception {
ParrotTable table = conn.getTable(Bean1.class);
Bean1 entityObj1 = new Bean1();
entityObj1.setUserName("JohnGao1");
entityObj1.setPassWord("123456");
Bean1 entityObj2 = new Bean1();
entityObj2.setUserName("JohnGao2");
entityObj2.setPassWord("123456");
List<Bean1> entityObjs = new ArrayList<Bean1>();
entityObjs.add(entityObj1);
entityObjs.add(entityObj2);
List<String> rowKeys = new ArrayList<String>();
rowKeys.add("key1");
rowKeys.add("key2");
table.insert(entityObjs, rowKeys);
}
@Test
publicvoid testUpdate() throws Exception {
ParrotTable table = conn.getTable(Bean1.class);
Bean1 entityObj = new Bean1();
entityObj.setUserName("JohnGao1");
entityObj.setPassWord("1111111");
table.update(entityObj, "key1");
}
@Test
publicvoid testUpdates() throws Exception {
ParrotTable table = conn.getTable(Bean1.class);
Bean1 entityObj1 = new Bean1();
entityObj1.setUserName("JohnGao1");
entityObj1.setPassWord("2222222");
Bean1 entityObj2 = new Bean1();
entityObj2.setUserName("JohnGao2");
entityObj2.setPassWord("33333333");
List<Bean1> entityObjs = new ArrayList<Bean1>();
entityObjs.add(entityObj1);
entityObjs.add(entityObj2);
List<String> rowKeys = new ArrayList<String>();
rowKeys.add("key1");
rowKeys.add("key2");
table.update(entityObjs, rowKeys);
}
@Test
publicvoid testDelete() throws Exception {
ParrotTable table = conn.getTable(Bean1.class);
table.delete("key1");
}
@Test
publicvoid testDeletes() throws Exception {
ParrotTable table = conn.getTable(Bean1.class);
List<String> rowKeys = new ArrayList<String>();
rowKeys.add("key1");
rowKeys.add("key2");
table.delete(rowKeys);
}
@Test
publicvoid testGet1() throws Exception {
ParrotTemplate template = conn.getTemplate();
Bean1 bean = (Bean1) template.get(Bean1.class, "key1");
System.out.println(bean.getUserName());
System.out.println(bean.getPassWord());
}
@Test
publicvoid testGet2() throws Exception {
ParrotTemplate template = conn.getTemplate();
List<String> rowKeys = new ArrayList<String>();
rowKeys.add("key1");
rowKeys.add("key2");
List<Bean1> beans = (List<Bean1>) template.get(Bean1.class, rowKeys);
for (Bean1 bean : beans) {
System.out.println(bean.getUserName());
System.out.println(bean.getPassWord());
}
}
@Test
publicvoid testGet3() throws Exception {
ParrotTemplate template = conn.getTemplate();
Object[] objs = template.get(Bean1.class,
new String[] { "key1", "key2" });
for (Object obj : objs) {
Bean1 bean = (Bean1) obj;
System.out.println(bean.getUserName());
System.out.println(bean.getPassWord());
}
}
@Test
publicvoid testfind1() throws Exception {
ParrotTemplate template = conn.getTemplate();
List<Bean1> beans = template.find(Bean1.class, "key", null, null);
for (Bean1 bean : beans) {
System.out.println(bean.getUserName());
System.out.println(bean.getPassWord());
}
}
@Test
publicvoid testfind2() throws Exception {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ParrotTemplate template = conn.getTemplate();
List<Bean1> beans = template.find(Bean1.class, "key",
format.parse("2014-05-23 00:00:00").getTime(),
format.parse("2014-05-24 00:00:00").getTime());
for (Bean1 bean : beans) {
System.out.println(bean.getUserName());
System.out.println(bean.getPassWord());
}
}
@Test
publicvoid testfind3() throws Exception {
ParrotTemplate template = conn.getTemplate();
List<Bean1> beans = template
.find(Bean1.class, "key", null, null, 0, 10);
for (Bean1 bean : beans) {
System.out.println(bean.getUserName());
System.out.println(bean.getPassWord());
}
}
@Test
publicvoid testfind4() throws Exception {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ParrotTemplate template = conn.getTemplate();
List<Bean1> beans = template.find(Bean1.class, "key",
format.parse("2014-05-22 00:00:00").getTime(),
format.parse("2014-05-23 00:00:00").getTime(), 0, 10);
for (Bean1 bean : beans) {
System.out.println(bean.getUserName());
System.out.println(bean.getPassWord());
}
}
上述程序中我们很好的使用了Parrot的对象特性对HBase进行了CRUD操作,那么ConnectionConfig中包含2个getTable()方法,上述程序中使用getTable()方法成功的获取到了一个ParrotTable对象,该对象代表着可以用于操作HBase Table的一个表对象。
如果需要执行查询,我们则可以使用ConnectionConfig的getTemplate()方法获取一个ParrotTemplate对象,不仅支持通过索引的get操作,也能够支持scan扫描的find方式。
当我们使用完ParrotTable或者ParrotTemplate后,Parrot会自动管理其中的连接,无需开发人员手动进行资源的释放,并且使用ParrotTemplate执行数据检索的时候,结果集会自动被映射到实体Bean中返回,而无需开发人员手动进行赋值操作。
除了对Table进行CRUD操作,我们还可以通过getTable()方法获取到MetaDateTable对象,该对象用于执行HBase中的元数据操作,比如表的创建和删除,使用方式如下所示:
@Test
publicvoid testDropTable() throws Exception {
MetaDataTable table = conn.getTable();
table.drop(Bean1.class);
}
@Test
publicvoid testCreateTable() throws Exception {
MetaDataTable table = conn.getTable();
table.create(Bean1.class);
}
@Test
publicvoid testCreateTables() throws Exception {
MetaDataTable table = conn.getTable();
List<Class> classes = new ArrayList<Class>();
classes.add(Bean1.class);
classes.add(Bean2.class);
table.create(classes);
}
@Test
publicvoid testDropTables() throws Exception {
MetaDataTable table = conn.getTable();
List<Class> classes = new ArrayList<Class>();
classes.add(Bean1.class);
classes.add(Bean2.class);
table.drop(classes);
}
四、Parrot所支持的原生HBase特性
当Parrot不满足于我们的业务场景时怎么办?这个时候可以使用ConnectionConfig接口中提供的getHTableInterface()方法,该方法会返回一个HTableInterface对象,该对象就是HBase的原生Table对象,开发人员可以使用该对象完成HBase的CRUD操作,如下所示:
@Test
publicvoid testHTableInterface() throws Exception {
/* HBase的原生操作 */
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ParrotTable parrotTable = conn.getTable(Bean1.class);
HTableInterface table = parrotTable.getHTableInterface();
Scan can = new Scan();
can.setStartRow(Bytes.toBytes("key"));
can.setTimeRange(format.parse("2014-05-23 00:00:00").getTime(), format
.parse("2014-05-24 00:00:00").getTime());
ResultScanner rts = table.getScanner(can);
Result rt = null;
while ((rt = rts.next()) != null) {
System.out.println(Bytes.toString(rt.getValue("info".getBytes(),
"userName".getBytes())));
System.out.println(Bytes.toString(rt.getValue("info".getBytes(),
"passWord".getBytes())));
}
parrotTable.setAutoFlush(table);
}
一旦你在程序中使用原生的HTableInterface,则默认不再受到Parrot的管理,所有的资源则需要自己释放,所有的赋值操作则需要自己手动完成,Parrot将不会再负责结果集映射。
五、Parrot中被废弃的事物操作
刚开始设计Parrot的时候,我的想象非常美好。那就是在Parrot中添加事物支持,哪怕是“伪事物”也可以。但这一切都是错误的开始。
我们知道HBase0.94版本之前,可以说是根本没有提供事物这个操作的,但0.94版本之后却可以支持弱事物,所谓弱事物指的就是支持同一行中的多个put或delete操作,但跨行、跨表、跨Region则不行,那么我当时的思路是提供Annotation的方式实现事物的AOP横切管理,当有多个事物执行的时候,无论是否在同一个表或者同一个region中,我们只需要做到put或delete失败就回滚,回滚的操作比较复杂,put的话就对应delete,delete就对应put,这样一来就无需顾虑是否是跨行、跨表、跨Region了。
想象是美好的,但现实的残酷是,delete失败后,执行回滚,我不仅要保存delete之前的数据状态,且回滚时,我如何保证数据正确落入到删除之前的region中?如何确保插入的时间戳与数据真实insert前是一致的?等等这些问题折腾了一天,所以彻底死心宣布Parrot废弃事物,目前只能等待后续HBase版本的更新支持是事物后,再说!
六、Parrot的设计实现;
没时间写了,以后再说,主要是没建模工具,懒得画图。
相关推荐
标题“hbase轻量级中间件simplehbase v0.1简介”揭示了我们要讨论的主题——SimpleHBase,它是一个针对Apache HBase的轻量级中间件,版本为v0.1。这个中间件可能是为了简化HBase的使用,提高开发效率而设计的。描述...
simplehbase是java和hbase之间的轻量级中间件。 主要包含以下功能。 数据类型映射:java类型和hbase的bytes之间的数据转换。 简单操作封装:封装了hbase的put,get,scan等操作为简单的java操作方式。 hbase ...
hbase-sdk是基于hbase-client和hbase-thrift的原生API封装的一款轻量级的HBase ORM框架。 针对HBase各版本API(1.x~2.x)间的差异,在其上剥离出了一层统一的抽象。并提供了以类SQL的方式来读写HBase表中的数据。对...
HBase 二级索引 HBase 二级索引是指在 HBase 之上建立的一种二级索引机制,用于提高查询效率。根据华为公布的 HBase 二级索引实现方案,本方案主要参照了该方案,设计了 HBase 的二级索引机制。 知识点一:HBase ...
二级索引在HBase中的应用,是为了弥补其一级索引(RowKey索引)的不足,它为非RowKey字段提供了快速访问的通道。 首先,我们需要理解HBase的一级索引。HBase的表数据是按照RowKey排序存储的,RowKey是唯一标识一条...
CDH 使用 Solr 实现 HBase 二级索引 在大数据处理中,HBase 是一种流行的 NoSQL 数据库,用于存储大量的数据。然而,在查询和检索数据时,HBase 的性能可能不太理想。这是因为 HBase 是基于 Key-Value 的存储方式,...
而Spring Data Hadoop是Spring框架的一部分,它提供了与Hadoop生态系统集成的工具,包括对HBase的操作支持。本篇文章将详细讲解如何利用Spring Data Hadoop中的HbaseTemplate来操作HBase。 首先,我们需要理解...
public class IndexBuilder3 extends Configured{ public static class MapperIndex extends TableMapper,Put>{ private String tableName; private String columnFamily; private String[] qualifiers;...
### 360HBASE二级索引的设计与实践 #### 背景 在大数据处理领域,特别是面对海量数据(如千亿级别的数据量)时,高效的数据存储与快速查询成为了关键的技术挑战之一。HBase作为一种分布式、面向列的NoSQL数据库...
《HBase权威指南》探讨了如何通过使用与...在HBase中集成MapReduce框架;了解如何调节集群、设计模式、拷贝表、导入批量数据和删除节点等。 《HBase权威指南》适合使用HBase进行数据库开发的高级数据库研发人员阅读
是一款轻量级的ORM框架,封装了对HBase数据表的读写操作和对集群的运维管理,并针对HBase1.x的API和2.xAPI的差异,做了统一的定义, 提供更加方便的调用API。同时,HQL的功能也已上线,提供了类SQL读写数据的能力,...
《hbase权威指南》探讨了如何通过使用与...在hbase中集成mapreduce框架;了解如何调节集群、设计模式、拷贝表、导入批量数据和删除节点等。, 《hbase权威指南》适合使用hbase进行数据库开发的高级数据库研发人员阅读
Apache HBase是一个基于Hadoop的分布式数据库,设计用于处理大规模数据集,提供实时读写访问。它是Bigtable的开源实现,灵感来源于Google的论文“Bigtable: A Distributed Storage System for Structured Data”。自...
- **MapReduce JOIN**:利用MapReduce框架实现JOIN逻辑,适用于大数据量的情况,但增加了编程复杂度和执行开销。 #### 遇到的问题及解决方案 - **写性能下降**:随着Region数量增加,写性能会受到影响。可以通过...
HortonWorks 介绍HBase的企业级应用,以及HBase最新特性 HortonWorks 介绍HBase的企业级应用,以及HBase最新特性 HortonWorks 介绍HBase的企业级应用,以及HBase最新特性