为什么选择 Spring data hadoop

👉 请点赞支持这款 全新设计的脚手架 ,让 Java 再次伟大!

spring-data-hadoop

hbase 常见的操作方式有以下三种:

Native Api

原生 api 操作繁琐,就像用 JDBC 操作关系型数据库一样,类似 flush、submit、close 的使用让人眼花缭乱。如果碰巧你的应用程序使用 java 开发,那就又多了一条不使用 Native Api 的理由 ------ 在问题周围兜一个圈子再解决才是真正的 java 式设计。

Phoenix

apache 提供的 phoenix 提供了 mybatis-like 的支持,帮你「隐藏」了行键的麻烦,还支持在 mapper 文件中像操作关系型数据库一样,直接创建某个字段的索引,以优化大量数据操作时可能产生的性能问题。

这实在是太「夸张」了。在我有限的认知中不记得 Hbase 原生支持这样的功能。尽管我的目的只是「简单的操作 Hbase」,却还是怀着对 phenix 项目的崇敬,下载了相关依赖导入项目中。

可是结果让我失望。hbase-client.jar、hbase-server.jar、phoenix-core .jar 这三者在导入项目后产生了大量的依赖冲突。在我尝试了各种冲突排查手段都无解正焦头烂额时,先哲的一句话将我从泥潭拉了出来:

当你在一件事情上遇到困难时,就放弃这件事。 ------ 鲁迅

Spring data hadoop

我的目的毕竟只是「简单的操作 Hbase」。而 spring data 使用 Hbase 所需配置不过一行,还没有讨厌的 checked exception,更不会提供某些「夸张」的功能,最重要的是没有依赖冲突!这些特点深深的打动了我。

如何配置 Spring data hadoop

配置 spring data hadoop 分 3 步。

第一步:导入依赖包。

exclusion 选项最好根据你自己的情况来配置,重点注意 hbase-client 相关的版本一定要使用 1.2.0 以上,不然可能会和项目中既存的 guava 包产生冲突。

xml 复制代码
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-hadoop</artifactId>
    <version>2.5.0.RELEASE</version>
    <exclusions>
        <exclusion>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-client</artifactId>
    <version>1.3.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
        <exclusion>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-x-discovery</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </exclusion>
    </exclusions>
</dependency>

第二步:编写相关配置。

spring 官方文档 Working with HBase 只提供了 xml 配置示例,这让我十分头疼,毕竟不是谁都有心情去看源码的。但考虑到 spring boot 的项目最好还是用 java configuration 为好,所以只好提供一个非官方配置供参考使用。

java 复制代码
public class HbaseConfiguration {
    @Bean
    public HbaseTemplate hbaseTemplate() {
        org.apache.hadoop.conf.Configuration configuration = HBaseConfiguration.create();
        configuration.set("hbase.zookeeper.quorum", "address1:port","address2:port");
        return new HbaseTemplate(configuration);
    }
}

第三步:开始运行。

java 复制代码
public class HbaseTemplateTest extends BaseTest {

    @Autowired
    private HbaseTemplate hbaseTemplate;

    @Test
    public void hbaseTest() {
        hbaseTemplate.put("zl_test", "lch", "lch", "test", "test2".getBytes());
    }
shell 复制代码
hbase(main):006:0> scan 'zl_test'
ROW                                          COLUMN+CELL
 lch                                         column=lch:test, timestamp=1556627058655, value=test2
1 row(s) in 0.2910 seconds

hbase(main):007:0>

Row key 怎么办?

如果说放弃 phoenix 带来了什么麻烦的话,那就是我们得自己考虑行键问题了。

如果你的项目属于「用户消费内容」且不涉及「高并发」,那基本上可以跳过这一小节。

如果你的项目属于「用户生成内容」或涉及「高并发」那就要好好考虑行键设计。

为写优化

管理 hbase 数据的最小逻辑单元 region ,是按照 rowkey 字典顺序来进行分区划分的。当频繁写入某些连续字符时,可能会造成同一 region 下写入数据过多的情况。

比如,在一个「用户生成内容」的应用程序中,常常使用时间戳作为 rowkey,这样就可能造成同一时间段内大量数据被写入到一个 region 下,产生热点效应。

为了使我们的写入平均分配到各 region 中,必须对 rowkey 做出一些处理。一个简便的方式便是利用散列算法与集群数量做加盐处理。

java 复制代码
Long now = LocalDateTime.now().toInstant(ZoneOffset.UTC).toEpochMilli();
String saltedInput = String.format("%s|%s", now.hashCode() % 6, now);
System.out.println(saltedInput);

sout:
4|1556729837940

大功告成,现在 0 ~ 9 会平均分配到不同的 region 分区上,避免了热点效应。

为读(扫描)优化

同样以「在一个用户生成内容的应用程序中使用时间戳作为 rowkey 的情况」作为示例。

为读(扫描)优化意味着你最好把所有的数据尽量放到同一个列族与同一个 region 分区中。

另外,使用倒序时间戳作为 rowkey 也是一个不错的选择。hfile 的有序特性使最新的时间戳数据总是保持在文件起始位,这样在读取操作时就避免了过多的硬盘读,也不需要做额外的排序工作。

到底应该为读还是为写?

回顾上面小节的内容,为写优化意味着在读操作上需要付出相应的代价。现在扫描操作不得不在所有的 region 分区上进行遍历了,这大大增加了 IO 开销。

而为读优化,和为写优化的做法背道而驰,容易造成写入时的热点效应。

是的,读与写无法同时兼顾。为了构建出完整的应用程序你需要做出取舍。

结语

虽然最终选择了使用 spring data 来操作 hbase,但这并不代表其他的方式都不好。每种工具都有自己的特点和不足,结合自己的实际情况做出选择才是最明智的做法。

相关推荐
lwprain2 分钟前
常用docker应用部署,wordpress、mysql、tomcat、nginx、redis
mysql·docker·tomcat
Viktor_Ye9 分钟前
高效集成易快报与金蝶应付单的方案
java·前端·数据库
hummhumm11 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
一二小选手16 分钟前
【Maven】IDEA创建Maven项目 Maven配置
java·maven
J老熊21 分钟前
JavaFX:简介、使用场景、常见问题及对比其他框架分析
java·开发语言·后端·面试·系统架构·软件工程
猿java26 分钟前
什么是 Hystrix?它的工作原理是什么?
java·微服务·面试
AuroraI'ncoding27 分钟前
时间请求参数、响应
java·后端·spring
所待.38343 分钟前
JavaEE之线程初阶(上)
java·java-ee
Winston Wood1 小时前
Java线程池详解
java·线程池·多线程·性能
手握风云-1 小时前
数据结构(Java版)第二期:包装类和泛型
java·开发语言·数据结构