1. 准备

Hudi支持Spark-2.x版本,你可以点击如下链接安装Spark,并使用pyspark启动

# pyspark
export PYSPARK_PYTHON=$(which python3)
spark-2.4.4-bin-hadoop2.7/bin/pyspark \
--packages org.apache.hudi:hudi-spark-bundle_2.11:0.5.1-incubating,org.apache.spark:spark-avro_2.11:2.4.4 \
--conf 'spark.serializer=org.apache.spark.serializer.KryoSerializer'
  • spark-avro模块需要在--packages显示指定
  • spark-avro和spark的版本必须匹配
  • 本示例中,由于依赖spark-avro_2.11,因此使用的是scala2.11构建hudi-spark-bundle,如果使用spark-avro_2.12,相应的需要使用hudi-spark-bundle_2.12

进行一些前置变量初始化

# pyspark
tableName = "hudi_trips_cow"
basePath = "file:///tmp/hudi_trips_cow"
dataGen = sc._jvm.org.apache.hudi.QuickstartUtils.DataGenerator()

其中DataGenerator可以用来基于行程schema生成插入和删除的样例数据。

2. 插入数据

生成一些新的行程数据,加载到DataFrame中,并将DataFrame写入Hudi表

# pyspark
inserts = sc._jvm.org.apache.hudi.QuickstartUtils.convertToStringList(dataGen.generateInserts(10))
df = spark.read.json(spark.sparkContext.parallelize(inserts, 2)) hudi_options = {
'hoodie.table.name': tableName,
'hoodie.datasource.write.recordkey.field': 'uuid',
'hoodie.datasource.write.partitionpath.field': 'partitionpath',
'hoodie.datasource.write.table.name': tableName,
'hoodie.datasource.write.operation': 'insert',
'hoodie.datasource.write.precombine.field': 'ts',
'hoodie.upsert.shuffle.parallelism': 2,
'hoodie.insert.shuffle.parallelism': 2
} df.write.format("hudi"). \
options(**hudi_options). \
mode("overwrite"). \
save(basePath)

mode(Overwrite)会覆盖并重新创建数据集。示例中提供了一个主键 (schema中的uuid),分区字段(region/county/city)和组合字段(schema中的ts) 以确保行程记录在每个分区中都是唯一的。

3. 查询数据

将数据加载至DataFrame

# pyspark
tripsSnapshotDF = spark. \
read. \
format("hudi"). \
load(basePath + "/*/*/*/*") tripsSnapshotDF.createOrReplaceTempView("hudi_trips_snapshot") spark.sql("select fare, begin_lon, begin_lat, ts from hudi_trips_snapshot where fare > 20.0").show()
spark.sql("select _hoodie_commit_time, _hoodie_record_key, _hoodie_partition_path, rider, driver, fare from hudi_trips_snapshot").show()

该查询提供读取优化视图,由于我们的分区路径格式为region/country/city),从基本路径(basepath)开始,我们使用load(basePath + "/*/*/*/*")来加载数据。

4. 更新数据

与插入新数据类似,还是使用DataGenerator生成更新数据,然后使用DataFrame写入Hudi表。

# pyspark
updates = sc._jvm.org.apache.hudi.QuickstartUtils.convertToStringList(dataGen.generateUpdates(10))
df = spark.read.json(spark.sparkContext.parallelize(updates, 2))
df.write.format("hudi"). \
options(**hudi_options). \
mode("append"). \
save(basePath)

注意,现在保存模式现在为append。通常,除非是第一次尝试创建数据集,否则请始终使用追加模式。每个写操作都会生成一个新的由时间戳表示的commit

5. 增量查询

Hudi提供了增量拉取的能力,即可以拉取从指定commit时间之后的变更,如不指定结束时间,那么将会拉取最新的变更。

# pyspark
# reload data
spark. \
read. \
format("hudi"). \
load(basePath + "/*/*/*/*"). \
createOrReplaceTempView("hudi_trips_snapshot") commits = list(map(lambda row: row[0], spark.sql("select distinct(_hoodie_commit_time) as commitTime from hudi_trips_snapshot order by commitTime").limit(50).collect()))
beginTime = commits[len(commits) - 2] # commit time we are interested in # incrementally query data
incremental_read_options = {
'hoodie.datasource.query.type': 'incremental',
'hoodie.datasource.read.begin.instanttime': beginTime,
} tripsIncrementalDF = spark.read.format("hudi"). \
options(**incremental_read_options). \
load(basePath)
tripsIncrementalDF.createOrReplaceTempView("hudi_trips_incremental") spark.sql("select `_hoodie_commit_time`, fare, begin_lon, begin_lat, ts from hudi_trips_incremental where fare > 20.0").show()

这表示查询在开始时间提交之后的所有变更,此增量拉取功能可以在批量数据上构建流式管道。

6. 特定时间点查询

即如何查询特定时间的数据,可以通过将结束时间指向特定的提交时间,将开始时间指向”000”(表示最早的提交时间)来表示特定时间。

# pyspark
beginTime = "000" # Represents all commits > this time.
endTime = commits[len(commits) - 2] # query point in time data
point_in_time_read_options = {
'hoodie.datasource.query.type': 'incremental',
'hoodie.datasource.read.end.instanttime': endTime,
'hoodie.datasource.read.begin.instanttime': beginTime
} tripsPointInTimeDF = spark.read.format("hudi"). \
options(**point_in_time_read_options). \
load(basePath) tripsPointInTimeDF.createOrReplaceTempView("hudi_trips_point_in_time")
spark.sql("select `_hoodie_commit_time`, fare, begin_lon, begin_lat, ts from hudi_trips_point_in_time where fare > 20.0").show()

7. 删除数据

删除传入的HoodieKey集合,注意:删除操作只支持append模式

# pyspark
# fetch total records count
spark.sql("select uuid, partitionPath from hudi_trips_snapshot").count()
# fetch two records to be deleted
ds = spark.sql("select uuid, partitionPath from hudi_trips_snapshot").limit(2) # issue deletes
hudi_delete_options = {
'hoodie.table.name': tableName,
'hoodie.datasource.write.recordkey.field': 'uuid',
'hoodie.datasource.write.partitionpath.field': 'partitionpath',
'hoodie.datasource.write.table.name': tableName,
'hoodie.datasource.write.operation': 'delete',
'hoodie.datasource.write.precombine.field': 'ts',
'hoodie.upsert.shuffle.parallelism': 2,
'hoodie.insert.shuffle.parallelism': 2
} from pyspark.sql.functions import lit
deletes = list(map(lambda row: (row[0], row[1]), ds.collect()))
df = spark.sparkContext.parallelize(deletes).toDF(['partitionpath', 'uuid']).withColumn('ts', lit(0.0))
df.write.format("hudi"). \
options(**hudi_delete_options). \
mode("append"). \
save(basePath) # run the same read query as above.
roAfterDeleteViewDF = spark. \
read. \
format("hudi"). \
load(basePath + "/*/*/*/*")
roAfterDeleteViewDF.registerTempTable("hudi_trips_snapshot")
# fetch should return (total - 2) records
spark.sql("select uuid, partitionPath from hudi_trips_snapshot").count()

8. 总结

本篇博文展示了如何使用pyspark来插入、删除、更新Hudi表,有pyspark和Hudi需求的小伙伴不妨一试!

真香!PySpark整合Apache Hudi实战的更多相关文章

  1. 实战 | 将Apache Hudi数据集写入阿里云OSS

    1. 引入 云上对象存储的廉价让不少公司将其作为主要的存储方案,而Hudi作为数据湖解决方案,支持对象存储也是必不可少.之前AWS EMR已经内置集成Hudi,也意味着可以在S3上无缝使用Hudi.当 ...

  2. 实战| 配置DataDog监控Apache Hudi应用指标

    1. 可用性 在Hudi最新master分支,由Hudi活跃贡献者Raymond Xu贡献了DataDog监控Hudi应用指标,该功能将在0.6.0 版本发布,也感谢Raymond的投稿. 2. 简介 ...

  3. Apache Hudi + AWS S3 + Athena实战

    Apache Hudi在阿里巴巴集团.EMIS Health,LinkNovate,Tathastu.AI,腾讯,Uber内使用,并且由Amazon AWS EMR和Google云平台支持,最近Ama ...

  4. 阿里神器 Seata 实现 TCC模式 解决分布式事务,真香!

    今天这篇文章介绍一下Seata如何实现TCC事务模式,文章目录如下: 什么是TCC模式? TCC(Try Confirm Cancel)方案是一种应用层面侵入业务的两阶段提交.是目前最火的一种柔性事务 ...

  5. Apache Beam实战指南 | 手把手教你玩转KafkaIO与Flink

    https://mp.weixin.qq.com/s?__biz=MzU1NDA4NjU2MA==&mid=2247492538&idx=2&sn=9a2bd9fe2d7fd6 ...

  6. 《Apache kafka实战》读书笔记-kafka集群监控工具

    <Apache kafka实战>读书笔记-kafka集群监控工具 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 如官网所述,Kafka使用基于yammer metric ...

  7. SpringBoot与Shiro整合权限管理实战

    SpringBoot与Shiro整合权限管理实战 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] *观看本文章需要有一定SpringBoot整合经验* Shiro框架简介 Apach ...

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

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

  9. Apache Hudi和Presto的前世今生

    一篇由Apache Hudi PMC Bhavani Sudha Saktheeswaran和AWS Presto团队工程师Brandon Scheller分享Apache Hudi和Presto集成 ...

随机推荐

  1. E - Farthest Nodes in a Tree

    Given a tree (a connected graph with no cycles), you have to find the farthest nodes in the tree. Th ...

  2. BUUOJ [WUSTCTF2020]朴实无华

    [WUSTCTF2020]朴实无华 复现了武科大的一道题/// 进入界面 一个hack me 好吧,直接看看有没有robot.txt 哦豁,还真有 好吧 fAke_f1agggg.php 看了里面,然 ...

  3. [YII2] 自带分页调整

    在search Model的search()方法里有一个$dataProvider 属性 ,在这个属性数组里添加 'pagination' => ['pageSize' => 10,],设 ...

  4. CentOS7.7下二进制部署MySQL多版本多实例实战

    第一章 需求说明 部署MySQL5.7的三个多实例环境(端口分别为3307,3308,3309) 部署MySQL5.6和8.0版本数据库实例((端口分别为3316和3326) 第二章 环境准备 1.虚 ...

  5. linq深入

    一.匿名类:[ C# 3.0/.NET 3.x 新增特性 ] 1.1 不好意思,我匿了 在开发中,我们有时会像下面的代码一样声明一个匿名类:可以看出,在匿名类的语法中并没有为其命名,而是直接的一个ne ...

  6. python 工具链 包管理工具 pip

    Installation mac下可以采用 brew,easy_install(python自带)等方式安装. centos下可以采用yum,easy_install等方式安装. 但是上面两种方式在系 ...

  7. TensorFlow命令行参数FLAGS使用

    import os os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' import tensorflow as tf #tensorboard --logdir=&qu ...

  8. Java IO 流--FileUtils 工具类封装

    IO流的操作写多了,会发现都已一样的套路,为了使用方便我们可以模拟commosIo 封装一下自己的FileUtils 工具类: 1.封装文件拷贝: 文件拷贝需要输入输出流对接,通过输入流读取数据,然后 ...

  9. JavaScript中一种全新的数据类型-symbol

    连续连载了几篇<ES6对xxx的扩展>,本节咱们换换口味,介绍一种全新的数据类型:Symbol,中文意思为:标志,记号.音标:[ˈsɪmbəl]. 数据类型 在介绍Symbol之前,我们简 ...

  10. tp5--路由的使用(初级)

    在配置文件夹下的route.php文件配置路由: 控制器: 运行结果: