转摘请注明出处!!百度文库链接:http://wenku.baidu.com/view/c39c1456482fb4daa48d4b3e
HBase是一个分布式的、面向列的开源数据库.HBase不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库。另一个不同的是HBase基于列的而不是基于行的模式。HBase – Hadoop Database,是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用HBase技术可在廉价PC Server上搭建起大规模结构化存储集群。
在调控层的开发过程中,调控文件可以录入多个子规则,因此系统拆分出多个调控子规则,用于运价匹配查询时用。由于预先设计的不完善,调控文件和调控子规则都保存到hbase中,因此才会涉及到hbase分页和hbase排序问题,虽然解决了这个问题,但是觉得调控文件确实没必要放在hbase中,因为它不参与运价的匹配查询。
提示:关系型数据操作尽量不要用hbase作为数据库。
表名:目前表名定义”T_”+实体名大写
列族:列族定义不超过三个。目前只定义了一个列族”BASEINFO”
属性类型:推荐使用string。在传统的数据库中因为数据的大小不同,所以会用int、short、long、String、double来保存,定义数据格式的时候需要给定一个大小,但是在Hbase中,并不需要定义数据存储空间大小。
属性名:大写
主键:表名前缀+yyyyMMdd+4位序列号
备注:序号列从自增表中获取,每天定义重置为0.
表名和列族必须先初始化,该表才能使用。示例:create 'T_XCONTROL','BASEINFO'
代码参考:InitHBaseTable.properties InitHBaseTable
只需要保存最新版本的数据HColumnDescriptor.setMaxVersions(1)
是否使用压缩策略
数据自动过期,通过HColumnDescriptor.setTimeToLive(inttimeToLive)设置表中数据的存储生命期
介绍:hbase region split策略
是否使用预分区策略
是否需要写入内存
简单的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.class, controlTableName, 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
注意:所有比较运算符匹配的时候都是拿数据库的数据值跟设定值比较,而不是拿设定值去跟数据库的数据值比较
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的过滤器提供的比较器,在调控项目中我们用到是BinaryComparator、NullComparator、RegexStringComparator,下面详细说明下BinaryComparator、NullComparator使用情况
对于所有的比较运算符都可以使用,因此在等于、不等于、范围匹配的时候用它就可以了。
在判断为空或者不为空的时候会用到比较器。
在使用NullComparator需要注意的是,HBase对空的定义。举例说明:
row1的endarea是没有值的,但是在HBase中,它并表示空。
row2的endarea不存在该列,在HBase中,它表示空。
跟SubstringComparator比较器作用差不多,常用来做字符串匹配,与等于、不等于比较运算符结合使用,不能与范围(LESS、GREATER……)比较运算使用。
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=?)or(where 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嵌套,根据业务逻辑不同,嵌套的层级不同。
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
RegionServer中RPC监听器实例的数量。对于master来说,这个属性是master受理的处理线程(handler)数量。默认值是10。
根据调控层的业务场景,1条运价的匹配查询就会产生4条hbase并发查询。如果有20条,就可能有80条并发,这个并发量是相当的。除了将该参数适当调大可以增加并发处理能力外,还跟集群的数量和服务器的配置有直接的关系,预计集群数量越多,服务器CPU核数越高,并发处理能力越强。
HRegion是Hbase中分布式存储和负载均衡的最小单元。最小单元就表示不同的Hregion可以分布在不同的HRegion server上。但一个Hregion是不会拆分到多个server上的。
Hbase.hregion.max.filesize
HstoreFile的最大值。Region中任何一个列族的存储文件如果超过了这个上限,就会被拆分成两个region。默认:268435456(256x1024x1024),即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");