前言 常用的六种Hbase自带的比较器:
1.BinaryComparator 使用Bytes.compareTo()比较当前值与阈值
2.BinaryComparator 类似第一种,但它从左端开始前缀匹配
3.NullComparator 只判断当前值是否是null
4.BitComparator 通过位运算操作执行位级比较
5.RegexStringComparator 根据正则表达式去匹配
6.SubStringComparator 将数据转为string,使用string的contains方法进行匹配
注意:后三种比较器只能与EQUAL和NOT_EQUAL运算符搭配使用,即不能进行GREATER或LESS比较运算,会产生错误。
————摘自《Hbase权威指南》P131-P132
虽然HBASE提供了这么多自带的比较器,但个人感觉功能并不是很强大,比如:对行键中间连续几位进行一段区间式的查询,好似默认的比较器是无能为力的,只能采用自定义比较器实现(或许个人水平有限,未能发觉,如有不对,欢迎指正)。网上资料不多,过程一波三折.........,记之
1、搭建Protocol Buffers环境
使用过滤器执行查询时,比较器会被从执行的客户端传输到Hbase的服务端,最终在RegionServer上运行比较器的比较方法过滤的数据。而比较器从客户端传输到服务端,使用的序列化协议既不是json也不是xml,而是谷哥的protobuf!!! (1M内的文本效率极高 !)
1.1、下载protobuf-2.5.0解压,windows下额外下载protoc-2.5.0-win32,解压后将protoc.exe 放在protobuf-2.5.0的src目录下。
1.2、接着配置环境变量Path
cmd窗口使用protoc查看版本成功即可。
2、创建proto文件,使用protoc命令生成java代码
例:TimestampBitProtos.proto
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// This file contains protocol buffers that are used for filters
option java_package = "com.sky.cy.protos"; //生成java代码的包名
option java_outer_classname = "TimestampBitProtos"; //生成的类名
option java_generic_services = true;
option java_generate_equals_and_hash = true;
option optimize_for = SPEED;
// This file contains protocol buffers that are used for comparators (e.g. in filters)
message TimestampBitComparator {
required int32 skip=1;//自定义比较器中需序列化的字段
required string data=2;//自定义比较器中需序列化的字段
}
//生成java代码的包名
option java_outer_classname = "TimestampBitProtos"; //生成的类名
option java_generic_services = true;
option java_generate_equals_and_hash = true;
option optimize_for = SPEED;
// This file contains protocol buffers that are used for comparators (e.g. in filters)
message TimestampBitComparator {
required int32 skip=1;//自定义比较器中需序列化的字段
required string data=2;//自定义比较器中需序列化的字段
}
cmd窗口中命令生成java代码
命令:protoc.exe -I=D:/proto --java_out=D:/proto D:/proto/TimestampBitProtos.proto
自定义比较器的序列化类生成完毕!
如图:
3、创建比较器类,继承ByteArrayComparable,重写toByteArray、compareTo、parseFrom方法
import com.google.protobuf.InvalidProtocolBufferException;
import com.sky.cy.protos.TimestampBitProtos;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.exceptions.DeserializationException;
import org.apache.hadoop.hbase.filter.ByteArrayComparable;
import org.apache.hadoop.hbase.util.Bytes;
/**
* @描述: 自定义比较器
* @文件名: TimestampComparator.java
* @创建人: wangxinxin
* @创建时间: 2018/3/9
* @修改人: wangxinxin
* @修改备注:
*
*/
public class TimestampComparator extends ByteArrayComparable {
public static final Log LOG = LogFactory.getLog(TimestampComparator.class);
private byte[] data;
//跳过字节数
private int skip;
public TimestampComparator(byte[] data) {
super(data);
this.data = data;
}
/**
* @param data 目标时间戳
* @param skip 跳过字节数
*/
public TimestampComparator(byte[] data, int skip) {
super(data);
this.data = data;
this.skip = skip;
}
@Override
public byte[] toByteArray() {
TimestampBitProtos.TimestampBitComparator.Builder builder = TimestampBitProtos.TimestampBitComparator.newBuilder();
builder.setData(Bytes.toString(this.data));
builder.setSkip(this.skip);
return builder.build().toByteArray();
}
public static TimestampComparator parseFrom(byte[] pbBytes) throws DeserializationException {
TimestampBitProtos.TimestampBitComparator prop = null;
try {
prop = TimestampBitProtos.TimestampBitComparator.parseFrom(pbBytes);
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
return new TimestampComparator(Bytes.toBytes(prop.getData()), prop.getSkip());
}
@Override
public int compareTo(byte[] bytes, int offset, int len) {
if (len < 13) {
return 1;
} else {
/*byte转为long比较*/
/*long timeTarget = Long.parseLong(new String(data));
long timeInDB = Long.parseLong(new String(bytes, skip + offset, 13));
if (timeTarget < timeInDB) {
return -1;
} else if (timeTarget == timeInDB) {
return 0;
} else {
return 1;
}*/
for (int i = 0; i < data.length; i++) {
byte b1 = data[i];
byte b2 = bytes[i + offset + skip];
if (b1 < b2) {
return -1;
} else if (b1 > b2) {
return 1;
}
}
return 0;
}
}
}
按照自己的逻辑实现compareTo方法,比较器的比较逻辑所在。
使用protobuf生成的java类实现toByteArray方法,用于比较器的序列化。
使用protobuf生成的java类实现parseFrom方法,在RegionServer上反序列化使用。
自定义比较器类创建完毕!
4、将实现的自定义的比较器打包,放入Hbase的各RegionServer的lib目录下
不然使用自定义比较器会出现ClassNotFound错误。
(如有不对,欢迎指正!)
---------------------------------补充分割线2019年10月14日----------------------------
如今想来,当初想利用Hbase比较器实现复杂条件的查询的想法,貌似是错误的!
Hbase作为NoSql库,自身拥有高可扩展性,rowkey查询的高性能等特性,而复杂的业务条件,组合查询应该使用外部索引去实现更为妥当!(ES、Solr)