在《HBase Rowkey 设计指南》文章中,我们介绍了避免数据热点的三种比较常见方法:

  • 加盐 - Salting

  • 哈希 - Hashing

  • 反转 - Reversing

其中在加盐(Salting)的方法里面是这么描述的:给 Rowkey 分配一个随机前缀以使得它和之前排序不同。但是在 Rowkey 前面加了随机前缀,那么我们怎么将这些数据读出来呢?我将分三篇文章来介绍如何读取加盐之后的表,其中每篇文章提供一种方法,主要包括:

  • 使用协处理器读取加盐的表

  • 使用 Spark 读取加盐的表

  • 使用 MapReduce 读取加盐的表

关于协处理器的入门及实战,请参见《HBase 协处理器入门及实战》。本文使用的各组件版本:hadoop-2.7.7,hbase-2.0.4,jdk1.8.0_201。

测试数据准备

在介绍如何查询数据之前,我们先创建一张名为 iteblog 的 HBase 表,用于测试。为了数据均匀和介绍的方便,这里使用了预分区,并设置了27个分区,如下:


HBase 中加盐之后的表如何读取:协处理器篇_java

然后我们准备了1000000条测试数据。RowKey 的形式为 UID + 当前数据生成时间戳;由于 UID 的长度为4,所以1000000条数据会存在大量的 UID 一样的数据,所以我们使用加盐方法将这些数据均匀分散到上述27个 Region 里面。我们生成数据如下(数据生成的程序请参见:https://www.iteblog.com/archives/2507.html)

HBase 中加盐之后的表如何读取:协处理器篇_java_02

使用协处理器查询加盐之后的表

现在有数据了,我们需要查询所有 UID = 1000 的用户所有历史数据,那么如何查呢?我们知道 UID = 1000 的用户数据是均匀放到上述的27个 Region 里面的,因为经过加盐了,所以这些数据前缀都是类似于 A-,B-,C- 等开头的。其次我们需要知道,每个 Region 其实是有 Start Key 和 End Key 的,这些 Start Key 和 End Key 其实就是我们创建 iteblog 表指定的。如果你看了 《HBase 协处理器入门及实战》 这篇文章,你就知道协处理器的代码其实是在每个 Region 里面执行的;而这些代码在 Region 里面执行的时候是可以拿到当前 Region 的信息,包括了 Start Key 和 End Key,所以其实我们可以将拿到的 Start Key 信息和查询的 UID 进行拼接,这样就可以查询我们要的数据。协处理器处理篇就是基于这样的思想来查询加盐之后的数据的。

定义 proto 文件

为什么需要定义这个请参见 《HBase 协处理器入门及实战》 这篇文章。因为我们查询的时候需要传入查询的参数,比如tableName、 StartKey 、 EndKey 以及是否加盐等标记;同时当查询到结果的时候,我们还需要将数据返回,所以我们定义的 proto 文件如下:

HBase 中加盐之后的表如何读取:协处理器篇_java_03

然后我们使用 protobuf-maven-plugin 插件将上面的 proto 生成 java 类,具体如何操作参见 《在 IDEA 中使用 Maven 编译 proto 文件》(https://www.iteblog.com/archives/2509.html)。我们将生成的 DataQueryProtos.java 类拷贝到 com.iteblog.data.coprocessor.generated 包里面。

编写协处理器代码

有了请求和返回的类,现在我们需要编写协处理器的处理代码了,结合上面的分析,协处理器的代码实现如下:

HBase 中加盐之后的表如何读取:协处理器篇_java_04

HBase 中加盐之后的表如何读取:协处理器篇_java_05

大家可以看到,这里面的代码框架和 《HBase 协处理器入门及实战》 里面介绍的 HBase 提供的 RowCountEndpoint 示例代码很类似。主要逻辑在 queryByStartRowAndEndRow 函数实现里面。我们通过 DataQueryRequest 拿到客户端查询的表,StartKey 和 EndKey 等数据。通过 this.env.getRegion().getRegionInfo().getStartKey() 可以拿到当前 Region 的 StartKey,然后再和客户端传进来的 StartKey 和 EndKey 进行拼接就可以拿到完整的 Rowkey 前缀。剩下的查询就是正常的 HBase Scan 代码了。

现在我们将 IteblogTableDataSearch 类进行编译打包,并部署到 HBase 表里面去,具体如何部署参见 《HBase 协处理器入门及实战》

协处理器客户端代码编写

到这里,我们的协处理器服务器端的代码和部署已经完成了,现在我们需要编写协处理器客户端代码。其实也很简单,如下:

HBase 中加盐之后的表如何读取:协处理器篇_java_06

HBase 中加盐之后的表如何读取:协处理器篇_java_07

我们运行上面的代码,可以得到如下的输出:

HBase 中加盐之后的表如何读取:协处理器篇_java_08

可以看到,和我们使用 HBase Shell 输出的一致,而且我们还把所有的 UID = 1000 的数据拿到了。好了,到这里,使用协处理器查询 HBase 加盐之后的表已经算完成了,明天我将介绍使用 Spark 如何查询加盐之后的表,敬请关注 iteblog_hadoop 公众号。