Hudi 数据湖的插入,更新,查询,分析操作示例

作者:Grey

原文地址:

博客园:Hudi 数据湖的插入,更新,查询,分析操作示例

CSDN:Hudi 数据湖的插入,更新,查询,分析操作示例

前置工作

首先,需要先完成

Linux 下搭建 Kafka 环境

Linux 下搭建 Hadoop 环境

Linux 下搭建 HBase 环境

Linux 下搭建 Hive 环境

本文基于上述四个环境已经搭建完成的基础上进行 Hudi 数据湖的插入,更新,查询操作。

开发环境

Scala 2.11.8

JDK 1.8

需要熟悉 Maven 构建项目和 Scala 一些基础语法。

操作步骤

master 节点首先启动集群,执行:

  1. stop-dfs.sh && start-dfs.sh

启动 yarn,执行:

  1. stop-yarn.sh && start-yarn.sh

然后准备一个 Mave 项目,在 src/main/resources 目录下,将 Hadoop 的一些配置文件拷贝进来,分别是

$HADOOP_HOME/etc/hadoop/core-site.xml 文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
  3. <configuration>
  4. <property>
  5. <name>fs.default.name</name>
  6. <value>hdfs://master:9000</value>
  7. </property>
  8. <property>
  9. <name>hadoop.tmp.dir</name>
  10. <value>/usr/local/hadoop/tmp</value>
  11. </property>
  12. </configuration>

注意,需要在你访问集群的机器上配置 host 文件,这样才可以识别 master 节点。

$HADOOP_HOME/etc/hadoop/hdfs-site.xml 文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
  3. <configuration>
  4. <property>
  5. <name>dfs.replication</name>
  6. <value>1</value>
  7. </property>
  8. <property>
  9. <name>dfs.permissions</name>
  10. <value>false</value>
  11. </property>
  12. </configuration>

$HADOOP_HOME/etc/hadoop/yarn-site.xml 文件,目前还没有任何配置

  1. <?xml version="1.0"?>
  2. <configuration>
  3. </configuration>

然后,设计实体的数据结构,

  1. package git.snippet.entity
  2. case class MyEntity(uid: Int,
  3. uname: String,
  4. dt: String
  5. )

插入数据代码如下

  1. package git.snippet.test
  2. import git.snippet.entity.MyEntity
  3. import git.snippet.util.JsonUtil
  4. import org.apache.spark.SparkConf
  5. import org.apache.spark.sql.{SaveMode, SparkSession}
  6. object DataInsertion {
  7. def main(args: Array[String]): Unit = {
  8. System.setProperty("HADOOP_USER_NAME", "root")
  9. val sparkConf = new SparkConf().setAppName("MyFirstDataApp")
  10. .set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
  11. .setMaster("local[*]")
  12. val sparkSession = SparkSession.builder().config(sparkConf).enableHiveSupport().getOrCreate()
  13. val ssc = sparkSession.sparkContext
  14. ssc.hadoopConfiguration.set("dfs.client.use.datanode.hostname", "true")
  15. insertData(sparkSession)
  16. }
  17. def insertData(sparkSession: SparkSession) = {
  18. import org.apache.spark.sql.functions._
  19. import sparkSession.implicits._
  20. val commitTime = System.currentTimeMillis().toString //生成提交时间
  21. val df = sparkSession.read.text("/mydata/data1")
  22. .mapPartitions(partitions => {
  23. partitions.map(item => {
  24. val jsonObject = JsonUtil.getJsonData(item.getString(0))
  25. MyEntity(jsonObject.getIntValue("uid"), jsonObject.getString("uname"), jsonObject.getString("dt"))
  26. })
  27. })
  28. val result = df.withColumn("ts", lit(commitTime)) //添加ts 时间戳列
  29. .withColumn("uuid", col("uid"))
  30. .withColumn("hudipart", col("dt")) //增加hudi分区列
  31. result.write.format("org.apache.hudi")
  32. .option("hoodie.insert.shuffle.parallelism", 2)
  33. .option("hoodie.upsert.shuffle.parallelism", 2)
  34. .option("PRECOMBINE_FIELD_OPT_KEY", "ts") //指定提交时间列
  35. .option("RECORDKEY_FIELD_OPT_KEY", "uuid") //指定uuid唯一标示列
  36. .option("hoodie.table.name", "myDataTable")
  37. .option("hoodie.datasource.write.partitionpath.field", "hudipart") //分区列
  38. .mode(SaveMode.Overwrite)
  39. .save("/snippet/data/hudi")
  40. }
  41. }

然后,在 master 节点先准备好数据

  1. vi data1

输入如下数据

  1. {'uid':1,'uname':'grey','dt':'2022/09'}
  2. {'uid':2,'uname':'tony','dt':'2022/10'}

然后创建文件目录,

  1. hdfs dfs -mkdir /mydata/

把 data1 放入目录下

  1. hdfs dfs -put data1 /mydata/

访问:http://192.168.100.130:50070/explorer.html#/mydata

可以查到这个数据

接下来执行插入数据的 scala 代码,执行完毕后,验证一下

访问:http://192.168.100.130:50070/explorer.html#/snippet/data/hudi/2022

可以查看到插入的数据

准备一个 data2 文件

  1. cp data1 data2 && vi data2

data2 的数据更新为

  1. {'uid':1,'uname':'grey1','dt':'2022/11'}
  2. {'uid':2,'uname':'tony1','dt':'2022/12'}

然后执行

  1. hdfs dfs -put data2 /mydata/

更新数据的代码,我们可以做如下调整,完整代码如下

  1. package git.snippet.test
  2. import git.snippet.entity.MyEntity
  3. import git.snippet.util.JsonUtil
  4. import org.apache.hudi.{DataSourceReadOptions, DataSourceWriteOptions}
  5. import org.apache.spark.SparkConf
  6. import org.apache.spark.sql.{SaveMode, SparkSession}
  7. object DataUpdate {
  8. def main(args: Array[String]): Unit = {
  9. System.setProperty("HADOOP_USER_NAME", "root")
  10. val sparkConf = new SparkConf().setAppName("MyFirstDataApp")
  11. .set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
  12. .setMaster("local[*]")
  13. val sparkSession = SparkSession.builder().config(sparkConf).enableHiveSupport().getOrCreate()
  14. val ssc = sparkSession.sparkContext
  15. ssc.hadoopConfiguration.set("dfs.client.use.datanode.hostname", "true")
  16. updateData(sparkSession)
  17. }
  18. def updateData(sparkSession: SparkSession) = {
  19. import org.apache.spark.sql.functions._
  20. import sparkSession.implicits._
  21. val commitTime = System.currentTimeMillis().toString //生成提交时间
  22. val df = sparkSession.read.text("/mydata/data2")
  23. .mapPartitions(partitions => {
  24. partitions.map(item => {
  25. val jsonObject = JsonUtil.getJsonData(item.getString(0))
  26. MyEntity(jsonObject.getIntValue("uid"), jsonObject.getString("uname"), jsonObject.getString("dt"))
  27. })
  28. })
  29. val result = df.withColumn("ts", lit(commitTime)) //添加ts 时间戳列
  30. .withColumn("uuid", col("uid")) //添加uuid 列
  31. .withColumn("hudipart", col("dt")) //增加hudi分区列
  32. result.write.format("org.apache.hudi")
  33. // .option(DataSourceWriteOptions.TABLE_TYPE_OPT_KEY, DataSourceWriteOptions.MOR_TABLE_TYPE_OPT_VAL)
  34. .option("hoodie.insert.shuffle.parallelism", 2)
  35. .option("hoodie.upsert.shuffle.parallelism", 2)
  36. .option("PRECOMBINE_FIELD_OPT_KEY", "ts") //指定提交时间列
  37. .option("RECORDKEY_FIELD_OPT_KEY", "uuid") //指定uuid唯一标示列
  38. .option("hoodie.table.name", "myDataTable")
  39. .option("hoodie.datasource.write.partitionpath.field", "hudipart") //分区列
  40. .mode(SaveMode.Append)
  41. .save("/snippet/data/hudi")
  42. }
  43. }

执行更新数据的代码。

验证一下,访问:http://192.168.100.130:50070/explorer.html#/snippet/data/hudi/2022

可以查看到更新的数据情况

数据查询的代码也很简单,完整代码如下

  1. package git.snippet.test
  2. import org.apache.spark.SparkConf
  3. import org.apache.spark.sql.SparkSession
  4. object DataQuery {
  5. def main(args: Array[String]): Unit = {
  6. System.setProperty("HADOOP_USER_NAME", "root")
  7. val sparkConf = new SparkConf().setAppName("MyFirstDataApp")
  8. .set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
  9. .setMaster("local[*]")
  10. val sparkSession = SparkSession.builder().config(sparkConf).enableHiveSupport().getOrCreate()
  11. val ssc = sparkSession.sparkContext
  12. ssc.hadoopConfiguration.set("dfs.client.use.datanode.hostname", "true")
  13. queryData(sparkSession)
  14. }
  15. def queryData(sparkSession: SparkSession) = {
  16. val df = sparkSession.read.format("org.apache.hudi")
  17. .load("/snippet/data/hudi/*/*")
  18. df.show()
  19. println(df.count())
  20. }
  21. }

执行,输出以下信息,验证成功。

数据查询也支持很多查询条件,比如增量查询,按时间段查询等。

接下来是 flink 实时数据分析的服务,首先需要在 master 上启动 kafka,并创建 一个名字为 mytopic 的 topic,详见Linux 下搭建 Kafka 环境

相关命令如下

创建topic

  1. kafka-topics.sh --zookeeper 127.0.0.1:2181 --replication-factor 1 --partitions 1 --create --topic mytopic

生产者启动配置

  1. kafka-console-producer.sh --broker-list 127.0.0.1:9092 --topic mytopic

消费者启动配置

  1. kafka-console-consumer.sh --bootstrap-server 127.0.0.1:9092 --topic mytopic

然后运行如下代码

  1. package git.snippet.analyzer;
  2. import org.apache.flink.api.common.serialization.SimpleStringSchema;
  3. import org.apache.flink.streaming.api.datastream.DataStream;
  4. import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
  5. import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer;
  6. import java.util.Properties;
  7. public class DataAnalyzer {
  8. public static void main(String[] args) {
  9. final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
  10. Properties properties = new Properties();
  11. properties.setProperty("bootstrap.servers", "192.168.100.130:9092");
  12. properties.setProperty("group.id", "snippet");
  13. //构建FlinkKafkaConsumer
  14. FlinkKafkaConsumer<String> myConsumer = new FlinkKafkaConsumer<>("mytopic", new SimpleStringSchema(), properties);
  15. //指定偏移量
  16. myConsumer.setStartFromLatest();
  17. final DataStream<String> stream = env.addSource(myConsumer);
  18. env.enableCheckpointing(5000);
  19. stream.print();
  20. try {
  21. env.execute("DataAnalyzer");
  22. } catch (Exception e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. }

其中

  1. properties.setProperty("bootstrap.servers", "192.168.100.130:9092");

根据自己的配置调整,然后通过 kakfa 的生产者客户端输入一些数据,这边可以收到这个数据,验证完毕。

完整代码见

data-lake

Hudi 数据湖的插入,更新,查询,分析操作示例的更多相关文章

  1. Apache Hudi数据跳过技术加速查询高达50倍

    介绍 在 Hudi 0.10 中,我们引入了对高级数据布局优化技术的支持,例如 Z-order和希尔伯特空间填充曲线(作为新的聚类算法),即使在经常使用过滤器查询大表的复杂场景中,也可以在多个列而非单 ...

  2. C 线性表的链式存储实现及插入、删除等操作示例

    一.链式存储的优势 线性表的存储可以通过顺序存储或链式存储实现,其中顺序存储基于数组实现(见本人上一篇博客),在进行插入删除等操作时,需对表内某一部分元素逐个移动,效率较低.而链式结构不依赖于地址连续 ...

  3. C 线性表的顺序存储实现及插入、删除等操作示例

    一.线性表的定义 线性表(Linear List)是由同一类型元素构成的有序序列的线性结构.线性表中元素的个数称为线性表的长度:线性表内没有元素(长度为0)时,称为空表:表的起始位置称为表头,表的结束 ...

  4. 使用Apache Spark和Apache Hudi构建分析数据湖

    1. 引入 大多数现代数据湖都是基于某种分布式文件系统(DFS),如HDFS或基于云的存储,如AWS S3构建的.遵循的基本原则之一是文件的"一次写入多次读取"访问模型.这对于处理 ...

  5. 数据湖框架选型很纠结?一文了解Apache Hudi核心优势

    英文原文:https://hudi.apache.org/blog/hudi-indexing-mechanisms/ Apache Hudi使用索引来定位更删操作所在的文件组.对于Copy-On-W ...

  6. 使用Apache Hudi构建大规模、事务性数据湖

    一个近期由Hudi PMC & Uber Senior Engineering Manager Nishith Agarwal分享的Talk 关于Nishith Agarwal更详细的介绍,主 ...

  7. Apache Hudi:云数据湖解决方案

    1. 引入 开源Apache Hudi项目为Uber等大型组织提供流处理能力,每天可处理数据湖上的数十亿条记录. 随着世界各地的组织采用该技术,Apache开源数据湖项目已经日渐成熟. Apache ...

  8. 印度最大在线食品杂货公司Grofers的数据湖建设之路

    1. 起源 作为印度最大的在线杂货公司的数据工程师,我们面临的主要挑战之一是让数据在整个组织中的更易用.但当评估这一目标时,我们意识到数据管道频繁出现错误已经导致业务团队对数据失去信心,结果导致他们永 ...

  9. 构建数据湖上低延迟数据 Pipeline 的实践

    T 摘要 · 云原生与数据湖是当今大数据领域最热的 2 个话题,本文着重从为什么传统数仓 无法满足业务需求? 为何需要建设数据湖?数据湖整体技术架构.Apache Hudi 存储模式与视图.如何解决冷 ...

随机推荐

  1. Java8新特性: CompletableFuture详解

    CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步回调.流式处理.多个Future组合处理的能力,使Java在处理多任务的 ...

  2. postgresql逻辑备份工具pg_dump和pg_resotre学习

    (一)pg_dump备份 pg提供了pg_dump和pg_dumpall命令进行数据库的备份,pg_dumpall是将整个pg集群转储到一个脚本文件中,而pg_dump命令可以选择一个数据库或者部分表 ...

  3. java-分支结构(四种基本分支结构的认识)

    分支结构:有条件的执行某语句,并非每句必走 1)if结构:1条路 2)if...else结构:2条路 3)if...else if结构:多条路 4)switch...case结构:多条路 优点:效率高 ...

  4. Druid学习之查询语法

    写在前面 最近一段时间都在做druid实时数据查询的工作,本文简单将官网上的英文文档加上自己的理解翻译成中文,同时将自己遇到的问题及解决方法list下,防止遗忘. 本文的demo示例均来源于官网. D ...

  5. Docker 拉取Nginx镜像 和运行

    Docker 镜像拉取 docker pull [OPTIONS] NAME[:TAG|@DIGEST] 镜像拉取命令 OPTIONS说明: -a :拉取所有 tagged 镜像 --disable- ...

  6. Controller以及RestFul风格

    Controller以及RestFul风格 控制器Controller 控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义两种方式实现 控制器负责解析用户的请求并将其转换为一个模型 在Spr ...

  7. 利用userfaultfd + setxattr堆占位

    利用userfaultfd + setxattr堆占位 很久之前便看到过这个技术的名字,但是由于自己的摆烂,一直没有管.今天终于找到时间好好看一下这个技术的利用方式.利用userfaultfd + s ...

  8. 关于 JavaScript 中 null 的一切

    原文地址:Everything about null in JavaScript 原文作者:Dmitri Pavlutin 译者:Gopal JavaScript 有两种类型:原始类型(strings ...

  9. 手把手教你用Java获取IP归属地

    前几个月微信公众号上线了IP归属地的功能,后续知乎.抖音等平台纷纷添加了该功能.如果是国内的用户精确到省份,国外用户精确到国家.本文就使用Java实现获取IP归属地. ! 主要讲解几个步骤: Java ...

  10. 解决nuxt/koa架构初始项目运行报错问题

    今天在学习运用vue的nuxt/koa框架,初始化项目之后,在执行 $> npm run dev 时报错,错误详细信息如下: 点击查看报错的详细内容 > npm run dev Debug ...