Hbase Filter Scala 实战

4 篇文章 0 订阅
3 篇文章 0 订阅
大部分内容来自<hbase权威指南>,示例代码部分都用scala编写
介绍:
hbase的filter用来控制返回的数据,主要用在Get和Scan中,通过filter的限制可以指定返回列簇,列,时间戳和版本号.所有的filter都在服务端生效,叫做谓词下推.这样可以保证被过滤掉的数据不会被传送客户端.
在过滤层次结构的最底层是Filter接口和FilterBase抽象类,用户可以通过集成Filter和FilterBase实现自己的接口.使用getScan或get获取数据,返回一个ScannerResult或者Result,可以把,Result看做是一行,每个Result包装了KeyValue类,每个KeyValue类可以看做一个行列对应的值.

CompareFilter
CompareFilter是一组经常使用到的Filter,CompareFilter有一组子类,分别实现行,列簇,列....的过滤,在实例化CompareFilter的时候,需要传入两个参数.CompareOp和ByteArrayComparable.

CompareOp
CompareFilter.CompareOp.LESS匹配小于设置值的值
CompareFilter.CompareOp.LESS_OR_EQUAL匹配小于等于设置值的值
CompareFilter.CompareOp.EQUAL匹配等于设置值的值
CompareFilter.CompareOp.NOT_EQUAL匹配不等于设置值的值
CompareFilter.CompareOp.GREATER_OR_EQUAL匹配大于等于设置值的值
CompareFilter.CompareOp.GREATER匹配大于设置值的值
CompareFilter.CompareOp.NO_OP排除一切值

ByteArrayComparable实例
BinaryComparator使用Bytes.compareTo()比较当前值与阈值
BinaryPrefixComparator前缀匹配
NullComparator不做匹配,只判断当前值是否NULL
BitComparator通过BitwiseOp类提供的按位与(AND)/或(OR)/异或(XOR)操作执行位级比较
RegexStringComparator根据一个正则表达式,在实例化这个比较器的时候去匹配表中的数据
SubstringComparator把阈值和表中的数据当做是一个String实例,同时通过contains()操作匹配字符串
LongComparator

CompareFilter实例
RowFilter根据rowKey过滤数据,只留下符合匹配条件的行
FamilyFilter根据FamilyFilter过滤数据,留下符合条件的行和FamilyFiter
QualifierFilter根据列名过滤数据
ValueFilter根据值过滤数据
DependentColumnFilter选定一个参考列,使用参考列的时间戳作为过滤条件,过滤时,每一行每一列与参考列的时间戳进行比较,就是返回一起修改的列
构造函数如下:
DependentColumnFilter(final byte [] family, final byte[] qualifier,
final boolean dropDependentColumn, final CompareOp valueCompareOp,
final ByteArrayComparable valueComparator)
DependentColumnFilter(final byte [] family, final byte [] qualifier)
DependentColumnFilter(final byte [] family, final byte [] qualifier,
final boolean dropDependentColumn)
构造器有三个,dropDependentColumn表示判断时间戳的结果是返回还是剔除,另外valueCompareOp/valueComparator两个参数表示可以在过滤的时候,连value一起过滤

scala代码实例:
import scala.collection.JavaConverters._
val table = new HTable(HandleHbase. conf , tableName )

var scan = new Scan()
scan.addColumn(Bytes.
toBytes(
"cf0" ) , Bytes.toBytes( "qual6" ))

println(
"--------------------row filter BinaryComparator -------------------------" )
val rowFilter1 = new RowFilter(CompareFilter.CompareOp. LESS_OR_EQUAL ,
new
BinaryComparator(Bytes. toBytes( "row-22" )))
scan.setFilter(rowFilter1)
val rowScanner1 = table.getScanner(scan)
for (res <- rowScanner1.iterator().asScala){
println(res)
}
rowScanner1.close()

println(
"--------------------row filter SubstringComparator -------------------------" )
val rowFilter2 = new RowFilter(CompareFilter.CompareOp. EQUAL ,
new
SubstringComparator( "-3" ))
scan.setFilter(rowFilter2)
val rowScanner2 = table.getScanner(scan)
for (res <- rowScanner2.iterator().asScala){
println(res)
}
rowScanner2.close()


scan =
new Scan()
scan.setStartRow(Bytes.
toBytes(
"row-2" ))
scan.setStopRow(Bytes.
toBytes(
"row-3" ))
println(
"--------------------family filter BinaryComparator -------------------------" )
val familyFilter1 = new FamilyFilter(CompareFilter.CompareOp. EQUAL ,
new
BinaryComparator(Bytes. toBytes( "cf1" )))
scan.setFilter(familyFilter1)
val familyScanner1 = table.getScanner(scan)
for (res <- familyScanner1.iterator().asScala){
println(res)
}
familyScanner1.close()

println(
"--------------------qualifier filter BinaryComparator -------------------------" )
/** 不管列簇 , 只要有列 qual1 就输出 */
val qualifierFilter1 = new QualifierFilter(CompareFilter.CompareOp. EQUAL ,
new
BinaryComparator(Bytes. toBytes( "qual1" )))
scan.setFilter(qualifierFilter1)
val qualifierScanner1 = table.getScanner(scan)
for (res <- qualifierScanner1.iterator().asScala){
println(res)
}
qualifierScanner1.close()

println(
"--------------------value filter BinaryComparator -------------------------" )
val valueFilter1 = new ValueFilter(CompareFilter.CompareOp. EQUAL ,
new
BinaryComparator(Bytes. toBytes( "val2" )))
scan.setFilter(valueFilter1)
val valueScanner1 = table.getScanner(scan)
for (res <- valueScanner1.iterator().asScala){
println(res)
}
valueScanner1.close()


println(
"--------------------dependent column filter BinaryComparator -------------------------" )
/** 使用 cf0:qual1 这一列作为参考列 , 输出和这一列一起修改的列 */
val dependentFilter1 = new DependentColumnFilter(Bytes. toBytes( "cf0" ) , Bytes.toBytes( "qual1" ))
scan.setFilter(dependentFilter1)
val dependentScanner1 = table.getScanner(scan)
for (res <- dependentScanner1.iterator().asScala){
println(res)
}
dependentScanner1.close()


专用Filter
专用Filter直接继承自抽象类FilterBase,专用的就是扩展性比较差,处理某些特定情况的时候比较方便.
SingleColumnValueFilter用列簇/列/值作为匹配条件,只有匹配特定列簇,列和值的行会被保留,其余的会被剔除(通过setFilterIfMissing设置缺失对应列簇,列的是否会被保留)
SingleColumnValueExcludeFilter作用和SingleColumnValueFilter一样,不同的是作为对照的列是否会被保留下来
PrefixFilter匹配行前缀,前缀匹配的行会被保留下来,RowFilter可以实现这个功能,只是这个Filter用起来比较方便
PageFilter用户可以使用这个Filter对结果按行分页,这个Filter每次返回固定行数的匹配结果.
KeyOnlyFilter只返回KeyValue的键,不返回值
FirstKeyOnlyFilter返回每行第一列
InclusiveStopFilter在scan中,使用setStartRow和setStopRow的时候是前闭后开的,可以使用这个Filter将stopRow包括进来
TimestampsFilter可以设置多个时间版本,返回符合版本的值
ColumnCountGetFilter可以使用这个过滤器来限制每行返回多少列
ColumnPaginationFilter与PageFilter相似,可以对一行的所有列进行分页
ColumnPrefixFilter列前缀过滤
RandomRowFilter随机行过滤器
部分示例代码:
import scala.collection.JavaConverters._
val table = new HTable( hbaseHandle . conf , tableName )

val scan = new Scan()
scan.setStartRow(Bytes.
toBytes(
"row-1" ))
scan.setStopRow(Bytes.
toBytes(
"row-2" ))
println(
"--------------------1.single column value filter -------------------------" )
/** 以列簇,列,值作为判断条件 , 过滤剩下匹配的行 */
val singleColumnValueFilter = new SingleColumnValueFilter(Bytes. toBytes( "cf0" ) ,
Bytes.toBytes( "qual3" ) , CompareFilter.CompareOp. EQUAL ,new BinaryComparator(Bytes. toBytes( "val2" )))
singleColumnValueFilter.setFilterIfMissing(
true )
scan.setFilter(singleColumnValueFilter)
val singleColumnValueScanner = table.getScanner(scan)
for (res <- singleColumnValueScanner.iterator().asScala){
println(res)
}
singleColumnValueScanner.close()

println(
"--------------------2.single column value exclude filter -------------------------" )
/** 以列簇,列,值作为判断条件 , 过滤剩下匹配的行,作为匹配条件的列不再保留 */
val singleColumnValueExcludeFilter = new SingleColumnValueExcludeFilter(Bytes. toBytes( "cf0" ) ,
Bytes.toBytes( "qual3" ) , CompareFilter.CompareOp. EQUAL ,new BinaryComparator(Bytes. toBytes( "val2" )))
singleColumnValueExcludeFilter.setFilterIfMissing(
true )
scan.setFilter(singleColumnValueExcludeFilter)
val singleColumnValueExcludeScanner = table.getScanner(scan)
for (res <- singleColumnValueExcludeScanner.iterator().asScala){
println(res)
}
singleColumnValueExcludeScanner.close()

println(
"--------------------3.prefix filter -------------------------" )
/** 匹配行健前缀 */
val prefixFilter = new PrefixFilter(Bytes. toBytes( "row-11" ))
scan.setFilter(prefixFilter)
val prefixScanner = table.getScanner(scan)
for (res <- prefixScanner.iterator().asScala){
println(res)
}
prefixScanner.close()

附加过滤器(就是Filter的装饰类,给一个Filter附加而外的功能)
SkipFilter很多过滤器是默认保留空置的行的,这个过滤器装饰的Filter能够过滤空行
WhileMatchFilter这个过滤器和SkipFilter相似,但是在第一条过滤数据出现的时候,这个过滤器就会停止

FilterList类
这个类包装了一个List<Filter>,是Filter子类,能够用List<Filter>构造一个FilterList传给scan同时使用多个过滤器.
构造器:
FilterList(final List<Filter> rowFilters)
FilterList(final Filter... rowFilters)
FilterList(final Operator operator)
FilterList(final Operator operator, final List<Filter> rowFilters)
FilterList(final Operator operator, final Filter... rowFilters)
FilterList.Operator
MUST_PASS_ALL所有过滤器包含这个值,这个值才会被包含在结果中,相当于AND操作
MUST_PASS_ONE只要有一个过滤器包含这个值,那这个值就会包含在结果,相当于OR操作
测试代码:
import scala.collection.JavaConverters._
val table = new HTable(hbaseHandle.conf , tableName)
val scan = new Scan()
scan.setStartRow(Bytes.
toBytes(
"row-2" ))
scan.setStopRow(Bytes.
toBytes(
"row-3" ))
val familyFilter1 = new FamilyFilter(CompareFilter.CompareOp. EQUAL ,
new
BinaryComparator(Bytes. toBytes( "cf1" )))
val qualifierFilter1 = new QualifierFilter(CompareFilter.CompareOp. EQUAL ,
new
BinaryComparator(Bytes. toBytes( "qual1" )))
println(
"------------------test MUST_PASS_ALL---------------------" )
val filterList1 = new FilterList(FilterList.Operator. MUST_PASS_ALL , familyFilter1 , qualifierFilter1)
scan.setFilter(filterList1)
val filterScanner1 = table.getScanner(scan)
for (res <- filterScanner1.iterator().asScala){
println(res)
}
filterScanner1.close()
println(
"------------------test MUST_PASS_ONE---------------------" )
val filterList2 = new FilterList(FilterList.Operator. MUST_PASS_ONE , familyFilter1 , qualifierFilter1)
scan.setFilter(filterList2)
val filterScanner2 = table.getScanner(scan)
for (res <- filterScanner2.iterator().asScala){
println(res)
}
filterScanner2.close()

注意:
使用多个FilterList的组合,就能够使用类似在SQL中的多个AND和OR的查询了.

自定义Filter
自定义Filter需要继承Filter类,因为Filter在服务器端执行,所以使用自定义Filter的时候,注意还要部署到服务器上,在hbase_env.sh加入自定义Filter的jar包路径到HBASE_CLASSPATH.
以下使用到的Cell类是KeyValue的父类
Filter中定义了若干方法,方法的执行顺序如下:
public boolean filterRowKey(byte[] data, int offset, int length)
在这个方法中判断RowKey是否要过滤,返回true表示过滤这一行,返回false表示不过滤这一行.
public ReturnCode filterKeyValue(final Cell v)
上一个方法执行后,确定一行不过滤,这时候就可以逐个扫描一行的KeyValue(Cell)了,返回一个枚举类型ReturnCode,ReturnCode的返回类型有如下几个:
  • INCLUDE : 表示在结果中包含这个KeyValue(Cell)实例.
  • SKIP : 表示跳过这个KeyValue实例并继续处理接下来的工作.
  • NEXT_COL : 跳过当前列并继续处理后面的列.例如,TimestampsFilter使用了这个返回值.
  • NEXT_ROW : 与上面的相似,跳过当前行,并处理下一行.例如:RowFilter使用了这个返回值
  • SEEK_NEXT_USING_HINT : 一些过滤器要跳过一系列值,此时需要是用这个返回值通知并执行框架使用public Cell getNextCellHint(final Cell currentKV),来决定跳到什么位置.例如 : ColumnPrefixFilter使用了这个功能.
public void filterRowCells(List<Cell> kvs)
一旦所有的行和列经过前面两个方法的检查后,这个方法会被调用.本方法让用户可以访问之前两个方法筛选出来的KeyValue实例.DependentColumnFilter过滤器使用这个方法来过滤与参考列不匹配的数据.
public boolean filterRow()
以上所有方法执行完之后,filterRow会被执行.PageFilter使用当前方法来检查在一次迭代分页中返回的行数是否达到预期的页大小,如果达到页大小则返回True.默认返回值是false,此时结果中包含当前行.
public void reset()
在迭代器中为每个新行重置过滤器.
public boolean filterAllRemaining()
当这个返回True,可以用于结果整个扫描操作.可以使用这个方法减少扫描,优化结果.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 批量操作是指一次性对多个数据进行操作,可以提高操作效率。在使用 Spark 读写 HBase 时,也可以使用批量操作来提高效率。具体实现方式如下: 1. 批量写入数据 使用 HBase 的 Put 类来创建要写入的数据,然后将 Put 对象添加到一个 List 中,最后使用 HBaseTable 类的 put 方法来批量写入数据。示例代码如下: ```scala val conf = HBaseConfiguration.create() val connection = ConnectionFactory.createConnection(conf) val table = connection.getTable(TableName.valueOf("table_name")) val puts = new ListBuffer[Put]() for (i <- 1 to 100) { val put = new Put(Bytes.toBytes(s"row_$i")) put.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("col"), Bytes.toBytes(s"value_$i")) puts += put } table.put(puts.toList.asJava) ``` 2. 批量读取数据 使用 HBase 的 Get 类来创建要读取的数据,然后将 Get 对象添加到一个 List 中,最后使用 HBaseTable 类的 get 方法来批量读取数据。示例代码如下: ```scala val conf = HBaseConfiguration.create() val connection = ConnectionFactory.createConnection(conf) val table = connection.getTable(TableName.valueOf("table_name")) val gets = new ListBuffer[Get]() for (i <- 1 to 100) { val get = new Get(Bytes.toBytes(s"row_$i")) get.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("col")) gets += get } val results = table.get(gets.toList.asJava) for (result <- results) { val row = Bytes.toString(result.getRow) val value = Bytes.toString(result.getValue(Bytes.toBytes("cf"), Bytes.toBytes("col"))) println(s"$row: $value") } ``` 以上就是使用 Scala 实现 Spark 读写 HBase 的批量操作的方法。 ### 回答2: 在实际的数据处理中,一次需要对多条数据进行读写操作,如果每次都进行单条的读写逐条操作会使程序效率非常低下。所以spark提供了批量操作API,可以对多条数据进行一次性的读写操作,极大地提高了程序的效率。 批量读操作: 批量读取数据的方式有两种:Get和Scan。 使用Get方式读取多条数据,需要将每条数据对应的Get对象添加到List集合当中,再将List集合转换为RDD对象进行操作。示例代码如下: ```scala val conf = HBaseConfiguration.create() conf.set(TableInputFormat.INPUT_TABLE, tableName) val gets = new util.ArrayList[Get]() gets.add(new Get(Bytes.toBytes("rowkey1"))) gets.add(new Get(Bytes.toBytes("rowkey2"))) gets.add(new Get(Bytes.toBytes("rowkey3"))) conf.set(TableInputFormat.SCAN, convertScanToString(new Scan())) val getRdd = sc.parallelize(gets) val hbaseRdd = getRdd.map((_, null)).hbaseBulkGet(conf, tableName, (result: Result) => { val kv: Array[Byte] = result.getValue(Bytes.toBytes(family), Bytes.toBytes(column)) Bytes.toString(kv) }) println(hbaseRdd.collect.toBuffer) ``` 使用Scan方式读取多条数据,需要将Scan对象作为参数传入,再将RDD对象转换为PairRDD并使用hbaseScan方法进行操作。示例代码如下: ```scala val conf = HBaseConfiguration.create() conf.set(TableInputFormat.INPUT_TABLE, tableName) val scan = new Scan(Bytes.toBytes("rowkey1"), Bytes.toBytes("rowkey3")) conf.set(TableInputFormat.SCAN, convertScanToString(scan)) val hbaseRdd = sc.hbaseScanRDD(conf).map((result: Result) => { val kv: Array[Byte] = result.getValue(Bytes.toBytes(family), Bytes.toBytes(column)) Bytes.toString(kv) }) println(hbaseRdd.collect.toBuffer) ``` 批量写操作: 批量写操作可以使用Put对象集合,将多条数据对应的Put对象添加到集合中,并将集合转换成RDD进行操作即可。示例代码如下: ```scala val conf = HBaseConfiguration.create() conf.set(TableOutputFormat.OUTPUT_TABLE, tableName) val puts = new util.ArrayList[Put]() puts.add(new Put(Bytes.toBytes("rowkey1")).addColumn(Bytes.toBytes(family), Bytes.toBytes(column), Bytes.toBytes("value1"))) puts.add(new Put(Bytes.toBytes("rowkey2")).addColumn(Bytes.toBytes(family), Bytes.toBytes(column), Bytes.toBytes("value2"))) puts.add(new Put(Bytes.toBytes("rowkey3")).addColumn(Bytes.toBytes(family), Bytes.toBytes(column), Bytes.toBytes("value3"))) val putRdd = sc.parallelize(puts) putRdd.hbaseBulkPut(conf, tableName) ``` 总结: 批量操作是Spark访问HBase的常见操作方式,在实际的实现过程中需要注意以下几点: 1、Get和Scan对象在HBase中读取数据的方式不一样,需要注意区分; 2、使用批量读操作可以大大提高程序效率,减少读写操作的时间消耗; 3、使用批量写操作需要合理规划写入的数据,避免出现数据冲突问题,影响程序的运行。 ### 回答3: 本篇文章将继续深入介绍如何使用Scala编码实现Spark读写操作HBase,具体涉及到HBase的批量操作。 一、Batch操作概述 在使用HBase进行数据处理的时候,我们常常需要对一个或多个表进行批量操作,批量操作即是针对 HBase的多行进行插入、删除等操作,以此来实现在HBase操作上的高效处理。HBase提供了很多批量操作API,比如 Put、Get、Delete、Scan,这些API都是可以批量操作的。 在Spark中,我们同样可以使用类似的API对HBase进行批量操作。本文将根据具体需求使用Spark实现HBase的批量操作。 二、批量操作的实现 Spark读写HBase时,使用RDD中的foreachPartition来对每个分区进行处理,在该函数内使用HBase API进行操作。关于批量操作,我们可以在每个分区中开启一个batch操作,将每个操作加入batch后,再提交即可。 例如,我们可以考虑实现一个批量put的功能,将RDD中的数据一批一批写入表中: ``` def insert(tableName: String, rdd: RDD[(String, String)]): Unit = { try{ rdd.foreachPartition({ iter => val conf = HBaseUtils.getHBaseConfiguration() conf.set(TableOutputFormat.OUTPUT_TABLE, tableName) val conn = ConnectionFactory.createConnection(conf) val table = conn.getTable(TableName.valueOf(tableName)) val puts = new java.util.ArrayList[Put]() iter.foreach { case (rowKey:String, value: String) => { // 构造put对象并append val put = new Put(Bytes.toBytes(rowKey)) put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(columnQualifier), Bytes.toBytes(value)) puts.add(put) if (puts.size() >= batchSize) { // 多条记录组成的put对象,使用put(List<Put>)一次性写入 table.put(puts) puts.clear() } } } // 如果puts还有内容,再写一次 if (puts.size() > 0) { table.put(puts) puts.clear() } table.close() conn.close() }) } catch { case e: Exception => e.printStackTrace() } } ``` 在该方法中,我们使用foreachPartition遍历RDD中的每个分区,然后通过Connection来获取HBase表实例。 之后定义了一个用于存放Put的List,当List的大小大于等于batchSize时,就将这个List中的所有put操作提交给HBase执行。 最后,释放资源,并为大家展示如何调用这个方法: ``` val rdd: RDD[(String, String)] = ... val tableName: String = ... insert(tableName, rdd) ``` 使用这种方式实现批量put,我们可以将一批数据提交到HBase执行,从而提升写入效率。当然,对于其他批量操作也可以应用类似的方式。 三、总结 本文根据实际需求,结合Spark和HBase的特点,实现了一些常用的批量操作,为大家提供了一个快速、高效的HBase操作方案。批量操作的好处是,可以将多条记录一次性操作,请求与写入等待时间都会得到缩短,获得更高的效率。感兴趣的同学可以试试,在实际开发中应该会受益匪浅!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值