I know, i know 地球另一端有你陪我
一、过滤器
为了配合 hbase 查找数据时更加人性化,提供了过滤器进数据的过滤
普通的过滤器需要配合比较运算符和比较器进行使用
1、常见的比较运算符
-
LESS < -
LESS_OR_EQUAL <= -
EQUAL = -
NOT_EQUAL <> -
GREATER_OR_EQUAL >= -
GREATER > -
NO_OP 排除所有
2、常见的比较器
BinaryComparator
按字节索引顺序比较指定字节数组,采用Bytes.compareTo(byte[])
BinaryPrefixComparator
通BinaryComparator,只是比较左端前缀的数据是否相同
RegexStringComparator
提供一个正则的比较器,仅支持 EQUAL 和非EQUAL
SubstringComparator
判断提供的子串是否出现在中
3、常见过滤器
rowKey过滤器:RowFilter
列簇过滤器:FamilyFilter
列过滤器:QualifierFilter
列值过滤器:ValueFilter
4、专用过滤器
单列值过滤器:SingleColumnValueFilter
SingleColumnValueFilter会返回满足条件的cell所在行的所有cell的值(即会返回一行数据)
列值排除过滤器:SingleColumnValueExcludeFilter
与SingleColumnValueFilter类似,会排除掉指定的列,其他的列全部返回
rowkey前缀过滤器:PrefixFilter
比对前缀是否相同
分页过滤器PageFilter
近似 scan 中的 limit
5、多过滤器 FilterList
全在代码里
package day51;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
public class FilterTest {
Connection conn;
Admin admin;
TableName studentTN;
Table students;
@Before
public void createConn() throws IOException {
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "master:2181,node1:2181,node2:2181");
conn = ConnectionFactory.createConnection(conf);
admin = conn.getAdmin();
studentTN = TableName.valueOf("students");
students = conn.getTable(studentTN);
}
public void print(Filter filter) throws IOException {
Scan scan = new Scan();
scan.setFilter(filter);
ResultScanner scanner = students.getScanner(scan);
for (Result rs : scanner) {
String id = Bytes.toString(rs.getRow());
String name = Bytes.toString(rs.getValue("cf1".getBytes(), "name".getBytes()));
String age = Bytes.toString(rs.getValue("cf1".getBytes(), "age".getBytes()));
String gender = Bytes.toString(rs.getValue("cf1".getBytes(), "gender".getBytes()));
String clazz = Bytes.toString(rs.getValue("cf1".getBytes(), "clazz".getBytes()));
System.out.println(id + "," + name + "," + age + "," + gender + "," + clazz);
}
}
public void printCell(Filter filter) throws IOException{
Scan scan = new Scan();
scan.setFilter(filter);
ResultScanner scanner = students.getScanner(scan);
for (Result rs : scanner) {
for (Cell cell : rs.listCells()) {
String id = Bytes.toString(CellUtil.cloneRow(cell));
String f = Bytes.toString(CellUtil.cloneFamily(cell));
String q = Bytes.toString(CellUtil.cloneQualifier(cell));
String v = Bytes.toString(CellUtil.cloneValue(cell));
System.out.println(id + "," + f + ":" + q + " " + v);
}
}
}
@Test
public void BinaryComparatorTest() throws IOException {
BinaryComparator bct
= new BinaryComparator("23".getBytes());
ValueFilter vft
= new ValueFilter(CompareFilter.CompareOp.GREATER, bct);
print(vft);
}
@Test
public void SingleColumnValueFilterTest() throws IOException {
SingleColumnValueFilter scvft = new SingleColumnValueFilter(
"cf1".getBytes(), "age".getBytes(),
CompareFilter.CompareOp.GREATER,
"23".getBytes());
print(scvft);
}
@Test
public void SingleColumnValueExcludeFilterTest()
throws IOException {
SingleColumnValueExcludeFilter scveft
= new SingleColumnValueExcludeFilter(
"cf1".getBytes(), "age".getBytes(),
CompareFilter.CompareOp.GREATER,
"23".getBytes());
print(scveft);
}
@Test
public void BinaryPrefixComparator() throws IOException {
BinaryPrefixComparator bpct
= new BinaryPrefixComparator("15001009".getBytes());
RowFilter rft
= new RowFilter(CompareFilter.CompareOp.EQUAL, bpct);
print(rft);
}
@Test
public void PrefixFilter() throws IOException {
PrefixFilter pfft = new PrefixFilter("15001009".getBytes());
print(pfft);
}
@Test
public void RegexStringComparatorTest() throws IOException {
RegexStringComparator rsct
= new RegexStringComparator("[a-z]+[0-9]+");
FamilyFilter fft
= new FamilyFilter(CompareFilter.CompareOp.EQUAL, rsct);
printCell(fft);
}
@Test
public void SubStringComparatorTest() throws IOException {
SubstringComparator ssct
= new SubstringComparator("na");
QualifierFilter qfft
= new QualifierFilter(CompareFilter.CompareOp.EQUAL, ssct);
printCell(qfft);
}
@Test
public void PageFilterTest() throws IOException {
PageFilter pft = new PageFilter(20);
print(pft);
}
@Test
public void pageTest1() throws IOException {
int page = 4;
int pagesize = 10;
int page_first = (page - 1) * pagesize + 1;
PageFilter pft = new PageFilter(page_first);
Scan scan = new Scan();
scan.setFilter(pft);
String rowkey = null;
ResultScanner scanner = students.getScanner(scan);
for (Result rs : scanner) {
rowkey = Bytes.toString(rs.getRow());
}
Scan scan1 = new Scan();
scan1.withStartRow(rowkey.getBytes());
PageFilter pageFilter2 = new PageFilter(pagesize);
scan1.setFilter(pageFilter2);
ResultScanner scanner2 = students.getScanner(scan1);
for (Result rs : scanner2) {
String id = Bytes.toString(rs.getRow());
String name = Bytes.toString(rs.getValue("cf1".getBytes(), "name".getBytes()));
String age = Bytes.toString(rs.getValue("cf1".getBytes(), "age".getBytes()));
String gender = Bytes.toString(rs.getValue("cf1".getBytes(), "gender".getBytes()));
String clazz = Bytes.toString(rs.getValue("cf1".getBytes(), "clazz".getBytes()));
System.out.println(id + "," + name + "," + age + "," + gender + "," + clazz);
}
}
@Test
public void pageTest2() throws IOException{
int page = 4;
int pagesize = 10;
int baseId = 1500100000;
int page_first = baseId + (page - 1) * pagesize + 1;
Scan scan = new Scan();
scan.withStartRow((page_first + "").getBytes());
scan.setLimit(pagesize);
ResultScanner scanner = students.getScanner(scan);
for (Result rs : scanner) {
String id = Bytes.toString(rs.getRow());
String name = Bytes.toString(
rs.getValue("cf1".getBytes(), "name".getBytes()));
String age = Bytes.toString(
rs.getValue("cf1".getBytes(), "age".getBytes()));
String gender = Bytes.toString(
rs.getValue("cf1".getBytes(), "gender".getBytes()));
String clazz = Bytes.toString(
rs.getValue("cf1".getBytes(), "clazz".getBytes()));
System.out.println(
id + "," + name + "," + age + "," + gender + "," + clazz);
}
}
@Test
public void FilterList() throws IOException {
FilterList filterList = new FilterList();
SingleColumnValueFilter filter1
= new SingleColumnValueFilter(
"cf1".getBytes(),
"age".getBytes(),
CompareFilter.CompareOp.EQUAL,
"23".getBytes());
SingleColumnValueFilter filter2
= new SingleColumnValueFilter(
"cf1".getBytes(),
"clazz".getBytes(),
CompareFilter.CompareOp.GREATER,
new BinaryPrefixComparator("文科".getBytes()));
SingleColumnValueFilter filter3
= new SingleColumnValueFilter(
"cf1".getBytes(),
"gender".getBytes(),
CompareFilter.CompareOp.EQUAL,
"女".getBytes());
filterList.addFilter(filter1);
filterList.addFilter(filter2);
filterList.addFilter(filter3);
print(filterList);
}
@After
public void close() throws IOException {
admin.close();
conn.close();
students.close();
}
}
关于布隆过滤器
Bloom Filter(布隆过滤器)是1970年由布隆提出,实际上是一个很长的二进制向量和一系列随机映射函数。
布隆过滤器可以用于检索一个元素是否在一个集合中,它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
在计算机科学中,我们常常会碰到时间换空间或者空间换时间的情况,即为了达到某一个方面的最优而牺牲另一个方面。Bloom Filter在时间空间这两个因素之外又引入了另一个因素:错误率。
在判断一个元素是否属于某个集合时,会有一定的错误率(宁杀错,不放过) 有可能把不属于这个集合的元素误认为属于这个集合(False Positive), 但不会把属于这个集合的元素误认为不属于这个集合(False Negative), 增加了错误率这个因素后,Bloom Filter 通过允许少量错误来节省大量的存储空间。
它的用法其实是很容易理解的,我们拿个HBase中应用的例子来说下,我们已经知道rowKey存放在HFile中,那么为了从一系列的HFile中查询某个rowkey,我们就可以通过 Bloom Filter 快速判断 rowkey 是否在这个HFile中,从而过滤掉大部分的HFile,减少需要扫描的Block
二进制向量使用数组来实现,初始状态下位数组每一位都为0 假如此时有一个集合S = {x1, x2, … xn},Bloom Filter使用k个独立的hash函数,分别将集合中的每一个元素映射到{1,…,m}的范围。对于任何一个元素,被映射到的数字作为对应的位数组的索引,该位会被置为1。比如元素x1被hash函数映射到数字8,那么位数组的第8位就会被置为1。
下图中集合S只有两个元素x和y,分别被3个hash函数进行映射,映射到的位置分别为(0,3,6)和(4,7,10),对应的位会被置为1 现在假如要判断另一个元素是否是在此集合中,只需要被这3个hash函数进行映射,查看对应的位置是否有0存在,如果有的话,表示此元素肯定不存在于这个集合,否则有可能存在。下图所示就表示z肯定不在集合{x,y}中
HFile 中和 Bloom Filter 相关的Block
Scanned Block Section(扫描HFile时被读取): Bloom Block
Load-on-open-section(regionServer启动时加载到内存): BloomFilter Meta Block、Bloom Index Block
Bloom Block:Bloom数据块,存储Bloom的位数组 Bloom Index Block:Bloom数据块的索引 BloomFilter Meta Block:从HFile角度看bloom数据块的一些元数据信息
HBase中每个HFile都有对应的位数组,KeyValue在写入HFile时会先经过几个hash函数的映射,映射后将对应的数组位改为1,get请求进来之后再进行hash映射,如果在对应数组位上存在0,说明该get请求查询的数据不在该HFile中。
HFile中的Bloom Block中存储的就是上面说得位数组,当HFile很大时,Data Block 就会很多,同时KeyValue也会很多,需要映射入位数组的rowKey也会很多,所以为了保证准确率,位数组就会相应越大,那Bloom Block也会越大,为了解决这个问题就出现了Bloom Index Block,一个HFile中有多个Bloom Block(位数组),根据rowKey拆分,一部分连续的Key使用一个位数组。这样查询rowKey就要先经过Bloom Index Block(在内存中)定位到Bloom Block,再把Bloom Block加载到内存,进行过滤。
|