HBase 中加盐(Salting)之后的表如何读取:Spark 篇
我们知道,HBase 为我们提供了 hbase-mapreduce 工程包含了读取 HBase 表的 InputFormat、OutputFormat 等类。这个工程的描述如下:
This module contains implementations of InputFormat, OutputFormat, Mapper, Reducer, etc which are needed for running MR jobs on tables, WALs, HFiles and other HBase specific constructs. It also contains a bunch of tools: RowCounter, ImportTsv, Import, Export, CompactionTool, ExportSnapshot, WALPlayer, etc.
我们也知道,虽然上面描述的是 MR jobs,但是 Spark 也是可以使用这些 InputFormat、OutputFormat 来读写 HBase 表的,如下:
val sparkSession = SparkSession.builder .appName("HBase") .getOrCreate()val conf = HBaseConfiguration.create()conf.set(TableInputFormat.INPUT_TABLE, "iteblog")val HBaseRdd = sparkSession.sparkContext.newAPIHadoopRDD(conf, classOf[TableInputFormat], classOf[ImmutableBytesWritable], classOf[Result])println(HBaseRdd.count()) |
上面程序使用 TableInputFormat 计算了 iteblog 表的总行数。如果我们想查询某个 UID 的所有历史记录如何实现呢?如果你查看 TableInputFormat 代码,你会发现其包含了很大参数设置:
hbase.mapreduce.inputtablehbase.mapreduce.splittablehbase.mapreduce.scanhbase.mapreduce.scan.row.starthbase.mapreduce.scan.row.stophbase.mapreduce.scan.column.familyhbase.mapreduce.scan.columnshbase.mapreduce.scan.timestamphbase.mapreduce.scan.timerange.starthbase.mapreduce.scan.timerange.endhbase.mapreduce.scan.maxversionshbase.mapreduce.scan.cacheblockshbase.mapreduce.scan.cachedrowshbase.mapreduce.scan.batchsizehbase.mapreduce.inputtable.shufflemaps |
其中 hbase.mapreduce.inputtable 就是需要查询的表,也就是上面 Spark 程序里面的 TableInputFormat.INPUT_TABLE。而 hbase.mapreduce.scan.row.start 和 hbase.mapreduce.scan.row.stop 分别对应的是需要查询的起止 Rowkey,所以我们可以利用这个信息来实现某个范围的数据查询。但是要注意的是,iteblog 这张表是加盐了,所以我们需要在 UID 之前加上一些前缀,否则是查询不到数据的。不过 TableInputFormat 并不能实现这个功能。那如何处理呢?答案是重写 TableInputFormat 的 getSplits 方法。
从名字也可以看出 getSplits 是计算有多少个 Splits。在 HBase 中,一个 Region 对应一个 Split,对应于 TableSplit 实现类。TableSplit 的构造是需要传入 startRow 和 endRow。startRow 和 endRow 对应的就是上面 hbase.mapreduce.scan.row.start 和 hbase.mapreduce.scan.row.stop 参数传进来的值,所以如果我们需要处理加盐表,就需要在这里实现。
另一方面,我们可以通过 RegionLocator 的 getStartEndKeys() 拿到某张表所有 Region 的 StartKeys 和 EndKeys 的。然后将拿到的 StartKey 和用户传进来的 hbase.mapreduce.scan.row.start 和 hbase.mapreduce.scan.row.stop 值进行拼接即可实现我们要的需求。根据这个思路,我们的代码就可以按照如下实现:
package com.iteblog.data.spark;import java.io.IOException;import java.util.ArrayList;import java.util.List;import com.google.common.base.Strings;import org.apache.hadoop.conf.Configuration;import org.apache.hadoop.hbase.TableName;import org.apache.hadoop.hbase.client.Connection;import org.apache.hadoop.hbase.client.ConnectionFactory;import org.apache.hadoop.hbase.client.RegionLocator;import org.apache.hadoop.hbase.mapreduce.TableInputFormat;import org.apache.hadoop.hbase.mapreduce.TableSplit;import org.apache.hadoop.hbase.util.Bytes;import org.apache.hadoop.hbase.util.Pair;import org.apache.hadoop.mapreduce.InputSplit;import org.apache.hadoop.mapreduce.JobContext;public class SaltRangeTableInputFormat extends TableInputFormat { @Override public List<InputSplit> getSplits(JobContext context) throws IOException { Configuration conf = context.getConfiguration(); String tableName = conf.get(TableInputFormat.INPUT_TABLE); if (Strings.isNullOrEmpty(tableName)) { throw new IOException("tableName must be provided."); } Connection connection = ConnectionFactory.createConnection(conf); val table = TableName.valueOf(tableName) RegionLocator regionLocator = connection.getRegionLocator(table); String scanStart = conf.get(TableInputFormat.SCAN_ROW_START); String scanStop = conf.get(TableInputFormat.SCAN_ROW_STOP); Pair<byte[][], byte[][]> keys = regionLocator.getStartEndKeys(); if (keys == null || keys.getFirst() == null || keys.getFirst().length == 0) { throw new RuntimeException("At least one region is expected"); } List<InputSplit> splits = new ArrayList<>(keys.getFirst().length); for (int i = 0; i < keys.getFirst().length; i++) { String regionLocation = getTableRegionLocation(regionLocator, keys.getFirst()[i]); String regionSalt = null; if (keys.getFirst()[i].length > 0) { regionSalt = Bytes.toString(keys.getFirst()[i]).split("-")[0]; } byte[] startRowKey = Bytes.toBytes(regionSalt + "-" + scanStart); byte[] endRowKey = Bytes.toBytes(regionSalt + "-" + scanStop); InputSplit split = new TableSplit(TableName.valueOf(tableName), startRowKey, endRowKey, regionLocation); splits.add(split); } return splits; } private String getTableRegionLocation(RegionLocator regionLocator, byte[] rowKey) throws IOException { return regionLocator.getRegionLocation(rowKey).getHostname(); }} |
然后我们同样查询 UID = 1000 的用户所有历史记录,那么我们的程序可以如下实现:
package com.iteblog.data.sparkimport org.apache.hadoop.hbase.HBaseConfigurationimport org.apache.hadoop.hbase.client.Resultimport org.apache.hadoop.hbase.io.ImmutableBytesWritableimport org.apache.hadoop.hbase.mapreduce.TableInputFormatimport org.apache.hadoop.hbase.util.Bytesimport org.apache.spark.sql.SparkSessionimport scala.collection.JavaConversions._object Spark { def main(args: Array[String]): Unit = { val sparkSession = SparkSession.builder .appName("HBase") .getOrCreate() val conf = HBaseConfiguration.create() conf.set(TableInputFormat.INPUT_TABLE, "iteblog") conf.set(TableInputFormat.SCAN_ROW_START, "1000") conf.set(TableInputFormat.SCAN_ROW_STOP, "1001") val HBaseRdd = sparkSession.sparkContext.newAPIHadoopRDD(conf, classOf[SaltRangeTableInputFormat], classOf[ImmutableBytesWritable], classOf[Result]) HBaseRdd.foreach { case (_, result) => val rowKey = Bytes.toString(result.getRow) val cell = result.listCells() cell.foreach { item => val family = Bytes.toString(item.getFamilyArray, item.getFamilyOffset, item.getFamilyLength) val qualifier = Bytes.toString(item.getQualifierArray, item.getQualifierOffset, item.getQualifierLength) val value = Bytes.toString(item.getValueArray, item.getValueOffset, item.getValueLength) println(rowKey + " \t " + "column=" + family + ":" + qualifier + ", " + "timestamp=" + item.getTimestamp + ", value=" + value) } } }} |
我们编译打包上面的程序,然后使用下面命令运行上述程序:
bin/spark-submit --class com.iteblog.data.spark.Spark --master yarn --deploy-mode cluster --driver-memory 2g --executor-memory 2g ~/hbase-1.0-SNAPSHOT.jar |
得到的结果如下:
A-1000-1550572395399 column=f:age, timestamp=1549091990253, value=54A-1000-1550572395399 column=f:uuid, timestamp=1549091990253, value=e9b10a9f-1218-43fd-bd01A-1000-1550572413799 column=f:age, timestamp=1549092008575, value=4A-1000-1550572413799 column=f:uuid, timestamp=1549092008575, value=181aa91e-5f1d-454c-959cA-1000-1550572414761 column=f:age, timestamp=1549092009531, value=33A-1000-1550572414761 column=f:uuid, timestamp=1549092009531, value=19aad8d3-621a-473c-8f9fB-1000-1550572388491 column=f:age, timestamp=1549091983276, value=1B-1000-1550572388491 column=f:uuid, timestamp=1549091983276, value=cf720efe-2ad2-48d6-81b8B-1000-1550572392922 column=f:age, timestamp=1549091987701, value=7B-1000-1550572392922 column=f:uuid, timestamp=1549091987701, value=8a047118-e130-48cb-adfe..... |
和前面文章使用 HBase Shell 输出结果一致。
HBase 中加盐(Salting)之后的表如何读取:Spark 篇的更多相关文章
- HBase 中加盐之后的表如何读取:Spark 篇
在 <HBase 中加盐之后的表如何读取:协处理器篇> 文章中介绍了使用协处理器来查询加盐之后的表,本文将介绍第二种方法来实现相同的功能. 我们知道,HBase 为我们提供了 hbase- ...
- HBase中加盐(Salting)之后的表如何读取:协处理器文章
我们介绍了避免数据斑点的三种比较常见方法: 加盐-盐腌 哈希-散列 反转-反转 其中在加盐(Salting)的方法里面是这么描述的:给Rowkey分配一个随机指针以使其和之前排序不同.但是在Rowke ...
- hbase数据加盐(Salting)存储与协处理器查询数据的方法
转自: https://blog.csdn.net/finad01/article/details/45952781 ----------------------------------------- ...
- MD5加密算法中的加盐值 ,和彩虹表攻击 防止彩虹表撞库
一.什么是彩虹表? 彩虹表(Rainbow Tables)就是一个庞大的.针对各种可能的字母组合预先计算好的哈希值的集合,不一定是针对MD5算法的,各种算法的都有,有了它可以快速的破解各类密码.越是复 ...
- hive和hbase本质区别——hbase本质是OLTP的nosql DB,而hive是OLAP 底层是hdfs,需从已有数据库同步数据到hdfs;hive可以用hbase中的数据,通过hive表映射到hbase表
对于hbase当前noSql数据库的一种,最常见的应用场景就是采集的网页数据的存储,由于是key-value型数据库,可以再扩展到各种key-value应用场景,如日志信息的存储,对于内容信息不需要完 ...
- abp架构中加载公共css样式表和公共js的文件目录位置
src\shared\helpers\LocalizedResourcesHelper.ts
- 将HBase中的表加载到hive中
两种方式加载hbase中的表到hive中,一是hive创建外部表关联hbase表数据,二是hive创建普通表将hbase的数据加载到本地 1. 创建外部表 hbase中已经有了一个test表,内容如下 ...
- [Phoenix] 四、加盐表
摘要: 在密码学中,加盐是指在散列之前将散列内容(例如:密码)的任意固定位置插入特定的字符串.这个在散列中加入字符串的方式称为“加盐”.其作用是让加盐后的散列结果和没有加盐的结果不相同,在不同的应用情 ...
- 一种简单的md5加盐加密的方法(防止彩虹表撞库)
md5加密(或者说摘要算法)大家都很熟悉了 就不解释了 现在很多数据库设计都喜欢用单向加密的方式保存密码,验证时对提交的密码再次加密之后做密文对比 /// <summary> 使用MD5加 ...
随机推荐
- CF #459 D. MADMAX
D. MADMAX time limit per test 1 second memory limit per test 256 megabytes input standard input outp ...
- 3、JSP中的Cookie 用于存储 web 页面的用户信息。
cookie 在平时生活中的运用 存储用户在网页上的登陆信息,包括账号和密码. 有的网站,登陆的时候,会出现一个选项,问你是否要一周内或者一个月内保持登陆状态.如果你选了,那么一周之内,都不需要再输入 ...
- 符合PSR-0规范的自动加载
PSR-0规范 命名空间必须与绝对路径一致 类名首字母必须大写 除去入口文件外,其他“.php”必须只有一个类 类文件必须自动载入,不采用include等 单一入口 1)目录结构 2)代码 入口文件i ...
- Vue中计算属性(computed)和监听属性函数watch的比较
1.计算属性适用的情形我们可能会有这样的需求,一个数据属性在它所依赖的属性发生变化时,也要发生变化,这种情况下,我们最好使用计算属性.例如在下面这个例子中,如果我们使用监听函数,代码就会变得有点冗余. ...
- Emiya家今天的饭 NOIP2019 (CSP?) 类DP好题 luoguP5664
luogu题目传送门! 首先,硬求可行方案数并不现实,因为不好求(去年考场就这么挂的,虽然那时候比现在更蒟). 在硬搞可行方案数不行之后,对题目要求的目标进行转换: 可行方案数 = 总方案数 - 不合 ...
- [Objective-C] 016_UI篇_UIView(上)
在我们使用app时屏幕上能看到的UI元素(按钮,列表,图片...),我们称之为视图,都是继承与UIView,它们通常有着位置,大小,背景颜色等属性,在appl中视图和窗口展示了应用的用户界面,同时负责 ...
- Linux下db2V10.5命令行安装超详细图文教程(附下载地址)
下载地址:https://pan.baidu.com/s/1GtF03x1FMF3IsGdSiBJu-g 提取码:8vfj 失效了发邮件:wells974@163.com 一.db2prereqche ...
- debug PHP程序(xdebug、IntelliJ IDEA)
之前写PHP程序的都是echo调试,今天感觉太麻烦了就想起研究一下IntelliJ IDEA如何调试PHP程序. 从网上查找了很多资料,大部分都提到在IDE里开启服务,一下就懵了,怎么启这么多服务呢. ...
- Java Spring Cloud 实战之路 - 1 创建项目
0. 前言 该项目使用Maven进行管理和构建,所以需要预先配置好Maven.嗯,在这个系列里就不做过多的介绍了. 1. 创建项目 先创建一个pom.xml 文件,添加以下内容: <?xml v ...
- pix三接口配置
拓扑 R1 R1#conf t Enter configuration commands, one per line. End with CNTL/Z. R1(config)#int f0/0 R1( ...