开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第24天
一、本篇笔记重点内容:
- 编程应用过滤器
- 编程实现数据删除
- 编程实现单行查询
- Hbase的优化
二、 详细知识点介绍
编程应用过滤器
前面提到的单行查询、集合查询是按照行键值或者列族、字段进行数据显示。但是,在大规模的HBase集群数据中去执行这样的查询,将会极为低效,因为返回的结果集合会是比较庞大的,不利于数据处理与分析。
在HBase中,没有提供像SQL查询语句那样方便的接口,但为方便数据精细化操作,提供了过滤器这样的接口,即对于查询结果,按指定的方法进行过滤后返回结果集,在Get、Scan中均支持过滤器。
过滤器的调用需要实例化Filter类,通过不同的过滤器进行实例化以达到不同的过滤目的。实例化后,在Get或Scan中通过setFilter方法加载过滤器,过滤器的基类是Filter。
在HBase中支持多种过滤器机制,比较常见的是比较过滤器CompareFilter类,在该类中定义了常用的比较运算符,即用设定的值进行比较,满足条件的记录被返回。所有比较运算符如下表所示。
除了上面的比较运算符外,还定义了比较器,常见的比较器包括:BinaryComparator(字节比较器)、BinaryPrefixComparator(字节前缀比较器)、RegexStringComparator(正则表达式比较器)、NullComparator(空值比较器)、BitComparator(比特位比较器)、SubstringComparator(子串比较器)等。
下面以字段名称过滤器(QualifierFilter)为例,设计一个方法,返回指定字段名的数据集。
Table table = connection.getTable(TableName.valueOf(tbname));
Scan scan = new Scan();
QualifierFilter fr = new QualifierFilter(CompareFilter.CompareOp.EQUAL,
new BinaryComparator(Bytes.toBytes(column)));
scan.setFilter(fr);
ResultScanner res = null;
try {
res = table.getScanner(scan);
for(Result r : res) {
for(KeyValue kv : r.list()) {
System.out.print("row:" + Bytes.toString(kv.getRow()));
System.out.print("\t" + Bytes.toString(kv.getFamily()));
System.out.print(":" + Bytes.toString(kv.getQualifier()));
System.out.print(" value:" + Bytes.toString(kv.getValue()));
}
}
}finally {
res.close();
}
}
编程实现数据删除
通过创建delete对象,删除指定表内记录(删除这里有些特别,也就是删除并不是马上将数据从表中删除。)
调用Table的delete(Delete delete)方法,可以删除表集数据,需要传入的参数为Delete类,在Delete类里面,设置可以删除的列对象。
类Delete用于格式化要删除的列对象,通过此对象设定删除对象的条件。它的常用方法如下表所示:
Hbase的优化
在这里主要介绍下Hbase的一些优化设计,当然企业中不能对这些优化都使用,优化需要根据具体业务实施。
6.9.1 表设计****
1) 预分区
默认情况下,在创建HBase表的时候会自动创建一个region分区,当导入数据的时候,所有的HBase客户端都向这一个region写数据,直到这个region足够大了才进行切分。这样就会影响部分效率,
想要加快批量写入效率,可以通过预先创建一些空的regions,这样当数据写入HBase时,会按照region分区情况,在集群内做数据的负载均衡。
2) Row Key的设计
行键检索表中的数据主要通过三种方式:
1)通过单个row key访问:按照某个row key键值进行get操作;
2)通过row key的range进行scan:通过设置startRowKey和endRowKey,在这个范围内进行扫描;
3)全表扫描:即直接扫描整张表中所有行记录。
在HBase中,row key可以是任意字符串,最大长度64KB,实际应用中一般为10~100bytes,存为byte[]字节数组,一般设计成定长的。
row key是按照字典序存储,因此,设计row key时,要充分利用这个排序特点,将经常一起读取的数据存储到一块,将最近可能会被访问的数据放在一块。
举个例子:如果最近写入HBase表中的数据是最可能被访问的,可以考虑将时间戳作为row key的一部分,由于是字典序排序,所以可以使用Long.MAX_VALUE - timestamp作为row key,这样能保证新写入的数据在读取时可以被快速命中。
设计规则:
1)越小越好
2)Rowkey的设计是要根据实际业务来
3)散列性
a) 取反 001 002 : 100 200 取反后,rowkey可能落在不同的region上
b) Hash rowkey取hash值后,可能会均匀分布在不同的region上
散列弊端:降低了范围查找的效率
3) 列族的设计
不要在一张表里定义太多的column family。目前Hbase并不能很好的处理超过2~3个column family的表。因为某个column family在flush的时候,它邻近的column family也会因关联效应被触发flush,最终导致系统产生更多的I/O。
4) 设置最大版本
创建表的时候,可以通过HColumnDescriptor.setMaxVersions(int maxVersions)设置表中数据的最大版本,如果只需要保存最新版本的数据,那么可以设置setMaxVersions(1)。
5) 存储生命周期
创建表的时候,可以通过HColumnDescriptor.setTimeToLive(int timeToLive)设置表中数据的存储生命期,过期数据将自动被删除,例如如果只需要存储最近两天的数据,那么可以设置setTimeToLive(2 * 24 * 60 * 60)。(相当于Linux中的Crontab任务)
6.9.2 写 表 操作****
6) HTable
1) 创建多个Htable客户端用于并发写的操作,提高写入数据的吞吐量
2) 参数优化
Auto Flush****
通过调用HTable.setAutoFlush(false)方法可以将HTable写客户端的自动flush关闭,这样可以批量写入数据到HBase,而不是有一条put就执行一次更新,只有当put填满客户端写缓存时,才实际向HBase服务端发起写请求。默认情况下auto flush是开启的。
Write Buffer****
通过调用HTable.setWriteBufferSize(writeBufferSize)方法可以设置HTable客户端的写buffer大小,如果新设置的buffer小于当前写buffer中的数据时,buffer将会被flush到服务端。其中,writeBufferSize的单位是byte字节数,可以根据实际写入数据量的多少来设置该值。
7) 批量写入
通过调用HTable.put(Put)方法可以将一个指定的row key记录写入HBase,同样HBase提供了另一个方法:通过调用HTable.put(List)方法可以将指定的row key列表,批量写入多行记录,这样做的好处是批量执行,只需要一次网络I/O开销,这对于对数据实时性要求高,网络传输RTT高的情景下可能带来明显的性能提升。
6.9.3 读 表 操作****
8) Htable
1)创建多个Htable客户端用于并发读的操作,提高读入数据的吞吐量
2)参数优化
Scanner Caching****
hbase.client.scanner.caching配置项可以设置HBase scanner一次从服务端抓取的数据条数,默认情况下一次一条。通过将其设置成一个合理的值,可以减少scan过程中next()的时间开销,代价是scanner需要通过客户端的内存来维持这些被cache的行记录。
Scan Attribute Selection****
scan时指定需要的Column Family,可以减少网络传输数据量,否则默认scan操作会返回整行所有Column Family的数据。
Close ResultScanner****
通过scan取完数据后,记得要关闭ResultScanner,否则RegionServer可能会出现问题(对应的Server资源无法释放)。
- 批量读
通过调用HTable.get(Get)方法可以根据一个指定的row key获取一行记录,同样HBase提供了另一个方法:通过调用HTable.get(List)方法可以根据一个指定的row key列表,批量获取多行记录,这样做的好处是批量执行,只需要一次网络I/O开销,这对于对数据实时性要求高而且网络传输RTT高的情景下可能带来明显的性能提升。