为什么选择 Spring data hadoop

阅读本文之前,请投票支持这款 全新设计的脚手架 ,让 Java 再次伟大!

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,但这并不代表其他的方式都不好。每种工具都有自己的特点和不足,结合自己的实际情况做出选择才是最明智的做法。

相关推荐
Themberfue几秒前
Java 网络原理 ①-IO多路复用 || 自定义协议 || XML || JSON
xml·java·开发语言·网络·计算机网络·json
wm10435 分钟前
JavaEE 3大组件 Listener Servlet Filter
java·servlet·java-ee
疯一样的码农34 分钟前
基于Spring Boot + Vue3实现的在线商品竞拍管理系统源码+文档
java·spring boot·后端
Y编程小白1 小时前
SpringBoot的pom.xml文件中,scope标签有几种配置?
xml·spring boot·后端
m0_748251352 小时前
【SpringBoot】日志文件
java·spring boot·spring
m0_748234712 小时前
Java-33 深入浅出 Spring - FactoryBean 和 BeanFactory BeanPostProcessor
java·开发语言·spring
知初~2 小时前
java相关学习文档或网站整理
java·开发语言·学习
码农小灰2 小时前
什么是缓存穿透、缓存击穿、缓存雪崩,在项目中是如何解决和预防?它们分别会带来什么危害?
java·缓存
a栋栋栋2 小时前
apifox
java·前端·javascript
A22743 小时前
Redis——数据淘汰策略
java·redis·缓存