HBase开发实践

该手册编于2014年9月,为本人在上家公司所著,主要参考HBase权威指南进行实践的总结,仅供参考!

转摘请注明出处!!百度文库链接:http://wenku.baidu.com/view/c39c1456482fb4daa48d4b3e

前言

HBase是一个分布式的、面向列的开源数据库.HBase不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库。另一个不同的是HBase基于列的而不是基于行的模式。HBase – Hadoop Database,是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用HBase技术可在廉价PC Server上搭建起大规模结构化存储集群。

在调控层的开发过程中,调控文件可以录入多个子规则,因此系统拆分出多个调控子规则,用于运价匹配查询时用。由于预先设计的不完善,调控文件和调控子规则都保存到hbase中,因此才会涉及到hbase分页和hbase排序问题,虽然解决了这个问题,但是觉得调控文件确实没必要放在hbase中,因为它不参与运价的匹配查询。

提示:关系型数据操作尽量不要用hbase作为数据库。

第一部分 数据结构

表定义

表名:目前表名定义”T_+实体名大写

列族:列族定义不超过三个。目前只定义了一个列族”BASEINFO

数据结构定义

属性类型:推荐使用string。在传统的数据库中因为数据的大小不同,所以会用intshortlongStringdouble来保存,定义数据格式的时候需要给定一个大小,但是在Hbase中,并不需要定义数据存储空间大小。

属性名:大写

主键

主键:表名前缀+yyyyMMdd+4位序列号

备注:序号列从自增表中获取,每天定义重置为0.

初始化

表名和列族必须先初始化,该表才能使用。示例:create 'T_XCONTROL','BASEINFO'

第二部分 表的初始化

代码参考:InitHBaseTable.properties InitHBaseTable

初始化策略 

保留最新版本

只需要保存最新版本的数据HColumnDescriptor.setMaxVersions(1)

压缩策略

是否使用压缩策略

自动过期

数据自动过期,通过HColumnDescriptor.setTimeToLive(inttimeToLive)设置表中数据的存储生命期

预分区

介绍:hbase region split策略 

是否使用预分区策略

写入内存

是否需要写入内存

 

 

第三部分 代码开发

CRUD操作

简单的CRUD操作,参考HBase权威指南(中文版).pdf,下面的是对HBase基本操作进行面向对象封装后的CRUD操作。所有以HBase作为存储数据库的DAO层,都继承HBaseDaoImpl类,下列是使用示例。

新增操作

public String add(XControl control) throws Exception {

String id = HBaseRowKeyUtil.getRowKey(controlTableName);

control.setId(id);

control.setStatus(Status.ADD.getValue());

        PutDelete pd=HBaseConvetorUtil.convetor(control,id);

        super.savePutDelete(controlTableName, pd);

        return id;

更新操作

public String update(XControl control) throws Exception {

String id = control.getId();

        PutDelete pd=HBaseConvetorUtil.convetor(control,id);

        super.savePutDelete(controlTableName, pd);

        return id;

    }

查询操作

public XControl getXControl(String id) throws Exception {

     return super.get(XControl.class,controlTableName, id);

    }

删除操作

public void delete(String id) throws IOException {

delete(controlTableName, id);

}

表实例池

创建HTable实例是一项非常耗时的操作,通常耗时数秒才能完成。在资源高度紧张的环境下,每秒都有几千个请求,为每个请求单独创建HTable实例根本行不通。用户应当在一开始创建实例,然后再客户端生命周期内不断复用他们。

但是,在多线程环境中重用HTable实例会出现其他问题。

客户端可以通过HTablePool类来解决这个问题。它只有一个目的,那就是为HBase集群提供客户端连接池。

HTablePool类的使用参考HTablePoolUtil 表实例池

获取实例:HTablePoolUtil.getHTablePoolUtil().getHTable(tableName);

关闭实例:HTablePoolUtil.getHTablePoolUtil().putHTable(tableName);

 

获取总数

获取总数利用了HBase协处理器功能

1.配置

在$HBASE_HOME/conf/hbase-site.xml添加一个配置项。我用的0.94版本自带的实现为AggregateImplementation,具体如下

 <property>
   <name>hbase.coprocessor.region.classes</name>
<value>org.apache.hadoop.hbase.coprocessor.AggregateImplementation</value>    
 </property>

若之前未配置此项,则配置完后,需要重启hbase方能生效。

2.客户端使用代码示例

//获得符合条件结果总数

    public Long getTotal(String tableName, Filter valueFilter){

        Scan scan=new Scan();

        if(null!=valueFilter){

         scan.setFilter(valueFilter);

        }

     AggregationClient aggregationClient = new AggregationClient(conf);  

        long rowCount = 0;  

        try {  

            scan.addColumn(Bytes.toBytes("BASEINFO"), null);//必须有此句,或者用addFamily(),否则出错,异常包含 ci ****  

            rowCount = aggregationClient.rowCount(Bytes.toBytes(tableName), null, scan);  

        } catch (Throwable e) {  

         e.printStackTrace();  

        }  

        return rowCount;

  }

 

列表分页

HBase的分页实现相对复杂一些。核心思想是结合分页过滤器PageFilter(pageSize)和查询设置开始行scan.setStartRow(lastRow)lastRow为上一次查询rowkey,需要注意的是该rowkey是一个数组,对应多字段的存储位置;

不同用户登录会产生不同lastRow,因此我们把lastRow存储到session中,参考PageLastRowCache 

  为了解耦,我们又把对lastRow操作封装到HBaseDaoImpl  ,以便开发写代码的时候不需要关心lastRow的操作。

 public PageInfo searchXControl(QueryControlRuleQO qo,Integer pageSize,Integer currteIndex) throws Exception {

     //条件过滤器

     FilterList filterList = new QueryControlRuleFilterList(qo).getFilterList();

     //获得符合条件结果总数

     Long total = getTotal(controlTableName, filterList);

     //过滤器集合

     FilterList fl=new FilterList();

     //分页过滤器

     Filter filter = new PageFilter(pageSize);

     fl.addFilter(filterList);

     fl.addFilter(filter);

        //封装结果集

     List<XControl> list = getList(XControl.classcontrolTableName, fl, currteIndex);

        log.info("--------------------- total : " + list.size());

        //返回结果集

PageInfo page = new PageInfo(total, list);

        return page;

    }

列表排序

hbase都是按照字典序进行排序的,也就是降序,在页面的表现就是最早的数据(rowkey最小的)排在前面。

目前的解决方案是:给主键增加一个外键关联表,外键的生成规则是

400000000000-主键号,比如主键是X201401110001,对应外键则是X198598889999,为了实现升序排序功能,保存实体的时候用X198598889999作为主键,页面查询的时候再从关联表中根据X198598889999获取X201401110001。

备注:需要对新增、删除、查询进行关联操作。

示例:

public String add(XControl control) throws Exception {

pkControlDao.addXControlFK(id);

}

public void delete(String id) throws Exception {

pkControlDao.deleteXControlFK(id);

}

public PageInfo searchXControl(QueryControlRuleQO qo,Integer pageSize,Integer currteIndex) throws Exception {

     //根据外键查询出匹配主键

     if(StringUtils.isNotBlank(qo.getId())){

     qo.setPKs(pkControlDao.getXControlPKs(qo.getId()));

     }

 

代码参考:HBaseRowKeyUtil   PKXControlDaoHBaseImpl 

第四部分 Hbase查询

比较运算符

注意:所有比较运算符匹配的时候都是拿数据库的数据值跟设定值比较,而不是拿设定值去跟数据库的数据值比较

LESS

匹配小于设定值的值

LESS_OR_EQUAL

匹配小于等于设定值的值

EQUAL

匹配等于设定值的值

NOT_EQUAL

匹配与设定值不相等的值

GREATER_OR_EQUAL

匹配大于或等于设定值的值

GREATER

匹配大于设定值的值

NO_OP

排除一切值

 

比较器介绍

BinaryComparator 

使用Bytes.compareTo()比较当前值与阀值

BinaryPrefixComparator 

与上面的相似,使用Bytes.compareTo()进行匹配,但是是从左端开始前缀匹配

NullComparator

不做匹配,只判断当前值是不是null

BitComparator

通过BitwiseOp类提供的按位与(AND)、或(OR)、异或(XOR)操作执行位级比较

RegexStringComparator 

根据一个正则表达式,在实例化这个比较器的时候去匹配表中的数据

SubstringComparator

把阀值和表中数据当String实例,同时通过contains()操作匹配字符串

 

上表示HBase对基于CompareFilter的过滤器提供的比较器,在调控项目中我们用到是BinaryComparatorNullComparatorRegexStringComparator,下面详细说明下BinaryComparatorNullComparator使用情况

BinaryComparator

对于所有的比较运算符都可以使用,因此在等于、不等于、范围匹配的时候用它就可以了。

NullComparator

在判断为空或者不为空的时候会用到比较器。

在使用NullComparator需要注意的是,HBase对空的定义。举例说明:

row1endarea是没有值的,但是在HBase中,它并表示空。

row2endarea不存在该列,在HBase中,它表示空。

 

RegexStringComparator

跟SubstringComparator比较器作用差不多,常用来做字符串匹配,与等于、不等于比较运算符结合使用,不能与范围(LESSGREATER……)比较运算使用。

常用过滤器介绍

HBase提供的过滤器有很多,详细参考HBase权威指南(中文版).pdf,在这次项目中用到的过滤器,我们主要用了SingleColumnValueFilter以及分页的时候用到PageFilter

过滤器应用实例

注意:这里提供的过滤器实例都是针对单列值进行过滤的。

范围过滤:小于、小于或等于、大于、大于或等于、大于等于或小于、小于或大于

值过滤:等于、不等于

字符串过滤:匹配、不匹配

空过滤:空、非空

 

代码参考FilterHelper 过滤帮助类

过滤器集合的使用

在传统数据库查询中,经常会用到where A like ? and B=?,或者是where A like ? or B=?。

HBase实现这个功能,需要用到FilterList,示例:

where A like ? and B=?可以这样写

FilterList andlist = new FilterList(Operator.MUST_PASS_ALL);

andlist.addFilter(FilterHelper.getRegexStringFilter(field_A, field_A_Value));

andlist.addFilter(FilterHelper.getEqualFilter(field_B, field_A_Value));

where A like ? or B=?可以这样写

FilterList andlist = new FilterList(Operator.MUST_PASS_ONE);

andlist.addFilter(FilterHelper.getRegexStringFilter(field_A, field_A_Value));

andlist.addFilter(FilterHelper.getEqualFilter(field_B, field_A_Value));

 

复杂查询过滤

在上面查询比较简单,但实际业务中经常遇到更复杂的查询。例如:where A  like ? and B=?)orwhere A  like ? Or B=?),与上面示例相比,其实是多了一层嵌套。

HBase中我们也可以嵌套FilterList来实现这种复杂的查询:

FilterList andlist = new FilterList(Operator.MUST_PASS_ALL);

andlist.addFilter(FilterHelper.getRegexStringFilter(field_A, field_A_Value));

andlist.addFilter(FilterHelper.getEqualFilter(field_B, field_A_Value));

FilterList orlist = new FilterList(Operator.MUST_PASS_ONE);

orlist.addFilter(FilterHelper.getRegexStringFilter(field_A, field_A_Value));

orlist.addFilter(FilterHelper.getEqualFilter(field_B, field_A_Value));

 

FilterList list = new FilterList(Operator.MUST_PASS_ONE);

list.addFilter(andlist);

list.addFilter(orlist);

在调控项目中我们用到很多这种FilterList嵌套,根据业务逻辑不同,嵌套的层级不同。

第五部分 HBase性能优化

查询缓存

Scan的caching属性默认值是1,意味着扫描器每次从region服务器抓取1条记录进行匹配。我们可以设置caching为比1大得多的值。例如,设置为500,则一次可以抓取500条,需要注意的是该值设得越大服务器的内存开销会越多。

HTableInterface hTable=getHTable(tableName);

 Scan scan=new Scan();

/*设置缓存*/

scan.setCaching(StaticConfig.getIControl_hbase_cache());

ResultScanner scanner= hTable.getScanner(scan);

多线程配置

Hbase.regionser.handler.count

RegionServerRPC监听器实例的数量。对于master来说,这个属性是master受理的处理线程(handler)数量。默认值是10。

根据调控层的业务场景,1条运价的匹配查询就会产生4hbase并发查询。如果有20条,就可能有80条并发,这个并发量是相当的。除了将该参数适当调大可以增加并发处理能力外,还跟集群的数量和服务器的配置有直接的关系,预计集群数量越多,服务器CPU核数越高,并发处理能力越强。

预分区

HRegion是Hbase中分布式存储和负载均衡的最小单元。最小单元就表示不同的Hregion可以分布在不同的HRegion server上。但一个Hregion是不会拆分到多个server上的。

Hbase.hregion.max.filesize

HstoreFile的最大值。Region中任何一个列族的存储文件如果超过了这个上限,就会被拆分成两个region默认:268435456256x1024x1024,即256M

我们的调控文件比较小,要达到分区最大上限256M需要较多的调控文件。为了提高并发量,我们需要在没有达到分区上限的情况下,产生多个hregion来保存和处理数据,这里就用hbase的预分区功能。

示例:

Configuration conf = HBaseConfiguration.create()

HBaseAdmin admin = new HBaseAdmin(conf);

HTableDescriptor desc = new HTableDescriptor(

Bytes.toBytes(tablename));

HColumnDescriptor coldef = new HColumnDescriptor(

Bytes.toBytes(colfamily));

admin.createTable(desc, Bytes.toBytes(1L), Bytes.toBytes(10L), 10);

//以第一位字符不同划分区

desc.setValue(HTableDescriptor.SPLIT_POLICY,

KeyPrefixRegionSplitPolicy.class.getName());

desc.setValue("prefix_split_key_policy.prefix_length", "1");

附录:代码

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值