HBase应用(4.3)列族高级配置

1.数据块大小

HBase会索引数据块的起始键,所以数据块越小索引越大,随机查找(性能更强;数据块越大索引越小,能够加载更多的索引数据。默认块大小为64K。

1
create 'test', {NAME => 'cf1', BLOCKSIZE => '65536'}

2.数据块缓存

2.1数据块缓存的内容

HBase的缓存分为两种:MemStore(写缓存) 和 BlockCache(读缓存),BlockCache对随机读取性能提升较为明显。数据块缓存包含以下内容:

  • DataBlock
  • row_key
  • meta数据
  • HFile索引
  • 布隆过滤器

如果某个列族只被顺序访问或很少被访问,可以关闭该列族的BlockCache,将缓存空间留给其他列族

1
create 'test', {NAME => 'cf1', BLOCKCACHE => 'false'}

2.2常用数据块缓存策略:

  1. LRUBlockCache(默认策略)

    使用ConcurrentHashMap实现,key为BlockKey,value为Block。LRUBlockCache在逻辑上分为3个区:Single-Access(25%)、Multi-Access(50%)、In-Memory(25%)。一个数据块被访问后被放入Single-Access,如果后续有更多请求就被放入Multi-Access。In-Memory是常驻内存(不保证),需要手动配置

    1
    create 'test', {NAME => 'cf1', IN_MEMORY => 'true'}

    配置In-Memory需要注意列族数据不要太大,否则会严重影响性能。

  2. CombinedBlockCache

    CombinedBlockCache由LRUBlockCache和BucketCache组合实现。当使用LRUBlockCache缓存淘汰率达到30-50%时可以考虑使用这种策略。

3.布隆过滤器

BloomFilter主要为了提升随机读(get)的性能,但是需要占用额外的索引空间。列限定符级布隆过滤器使用ROWCOL,行级布隆过滤器用ROW

1
create 'test', {NAME => 'cf1', BLOOMFILTER => 'ROWCOL'}

4.生存时间

对列族设置TTL(生存时间),超过该值的数据将在下一次大合并时删除:

1
create 'test', {NAME => 'cf1', TTL => '60'}

如果一个cell存在多个版本,超过TTL的版本将会在大合并时删除

5.压缩

HBase支持多种压缩方式,包括:LZO、Snappy和GZIP。压缩会导致读写数据文件时CPU占用提高。

1
create 'test', {NAME => 'cf1', COMPRESSION => 'SNAPPY'}

6.单元时间版本

HBase支持多时间版本数据

1
create 'test', {NAME => 'cf1', VERSIONS => '3'}

7.过滤器

过滤器可以将HBase返回给客户端的数据进行过滤,避免将所有数据传输到客户端后再进行筛选,可以有有效节省网络资源。

7.1自定义过滤器

实现一个自定义过滤器可以继承org.apache.hadoop.hbase.filter.Filter及其子类。

Filter中方法调用顺序:

HBase过滤器流程

假设有如下users表,需要使用自定义过滤器查询密码长度少于6位的用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package info.aviraer.demo.bigdata.core.hbase.chapter4;

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.exceptions.DeserializationException;
import org.apache.hadoop.hbase.filter.FilterBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;

public class MyFilter extends FilterBase implements Serializable{

public static Logger LOG = LoggerFactory.getLogger(MyFilter.class);
private boolean filterRow = false;

public byte [] toByteArray() throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
objectOutputStream.flush();
byteArrayOutputStream.flush();
return byteArrayOutputStream.toByteArray();
}

public static MyFilter parseFrom(final byte [] pbBytes) throws DeserializationException {
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(pbBytes));
return (MyFilter) objectInputStream.readObject();
} catch (ClassNotFoundException | IOException e) {
throw new DeserializationException(e);
}
}

@Override
public ReturnCode filterKeyValue(Cell v){
String qualifier = new String(CellUtil.cloneQualifier(v));
if ("password".equals(qualifier)){
String password = new String(CellUtil.cloneValue(v));
LOG.info("检查密码:" + password);
if (password.length() < 6){
return ReturnCode.SKIP; //返回信息不要包含密码
} else {
filterRow = true; //密码大于等于6位,过滤
return ReturnCode.NEXT_ROW;
}
} else {
LOG.info("跳过列:" + qualifier);
return ReturnCode.INCLUDE; //当前列限定符不是password
}
}

public boolean filterRow(){
return this.filterRow;
}

public void reset(){
this.filterRow = false;
}


public static void main(String[] args) throws IOException {
Configuration config = HBaseConfiguration.create();
//配置zookeeper集群
config.set("hbase.zookeeper.quorum", "cdh1,cdh2,cdh3");
Connection connection = ConnectionFactory.createConnection(config);

Table table = connection.getTable(TableName.valueOf("users"));

Scan scan = new Scan();
scan.setFilter(new MyFilter());
ResultScanner resultScanner = table.getScanner(scan);
System.out.println("密码不足6位的用户如下:");
for (Result result : resultScanner) {
System.out.println(new String(CellUtil.cloneValue(result.getColumnCells("info".getBytes(), "name".getBytes()).get(0))));
}

table.close();
connection.close();
}

}

编写完过滤器代码后要将代码打成jar包,并放到HBASE_CLASSPATH中。

自定义过滤器安装需要重启RegionServer,建议尽量使用内置过滤器

7.2内置过滤器

HBase有很多内置过滤器,介绍一些常用的过滤器

HBase内置过滤器

  1. 行过滤器:RowFilter

    基于行键过滤数据,支持精确匹配、字符串匹配、正则。

    1
    public RowFilter(final CompareOp rowCompareOp, final ByteArrayComparable rowComparator)

    rowCompareOp枚举支持以下类型:

    • LESS
    • LESS_OR_EQUAL
    • EQUAL
    • NOT_EQUAL
    • GREATER_OR_EQUAL
    • GREATER
    • NO_OP—默认返回flase,过滤所有

    ​ByteArrayComparable支持以下类型:
    ByteArrayComparable

    1
    2
    3
    Filter filter1 = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator(".*ack"));	//检查行键以ack结尾
    Filter filter2 = new RowFilter(CompareFilter.CompareOp.EQUAL, new SubstringComparator("a")); //检查行键包含a
    Filter filter3 = new RowFilter(CompareFilter.CompareOp.GREATER_OR_EQUAL, new BinaryComparator("aaa")); //检查行键大于等于aaa

  1. 前缀过滤器:PrefixFilter

    基于行键过滤。相当于自动为Scan计算一个stopRow。

    扫描users表中用户名以j开头的用户

    1
    2
    3
    String prefix = "j";
    Scan scan = new Scan(prefix.getBytes());
    scan.setFilter(new PrefixFilter(prefix.getBytes()));

  2. 限定符过滤器:QualifierFilter

    基于列限定符过滤。

  3. 值过滤器:ValueFilter

    基于Cell的值过滤。

  4. 时间戳过滤器:TimestampsFilter

    针对时间版本过滤。可以使用get或scan的getTimeRange替代。

  5. 过滤器列表:FilterList

    将多个filter按顺序插入,可以实现多个过滤器顺序执行。