真香!PySpark整合Apache Hudi实战
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实战的更多相关文章
- 实战 | 将Apache Hudi数据集写入阿里云OSS
		
1. 引入 云上对象存储的廉价让不少公司将其作为主要的存储方案,而Hudi作为数据湖解决方案,支持对象存储也是必不可少.之前AWS EMR已经内置集成Hudi,也意味着可以在S3上无缝使用Hudi.当 ...
 - 实战| 配置DataDog监控Apache Hudi应用指标
		
1. 可用性 在Hudi最新master分支,由Hudi活跃贡献者Raymond Xu贡献了DataDog监控Hudi应用指标,该功能将在0.6.0 版本发布,也感谢Raymond的投稿. 2. 简介 ...
 - Apache Hudi + AWS S3 + Athena实战
		
Apache Hudi在阿里巴巴集团.EMIS Health,LinkNovate,Tathastu.AI,腾讯,Uber内使用,并且由Amazon AWS EMR和Google云平台支持,最近Ama ...
 - 阿里神器 Seata 实现 TCC模式 解决分布式事务,真香!
		
今天这篇文章介绍一下Seata如何实现TCC事务模式,文章目录如下: 什么是TCC模式? TCC(Try Confirm Cancel)方案是一种应用层面侵入业务的两阶段提交.是目前最火的一种柔性事务 ...
 - Apache Beam实战指南 | 手把手教你玩转KafkaIO与Flink
		
https://mp.weixin.qq.com/s?__biz=MzU1NDA4NjU2MA==&mid=2247492538&idx=2&sn=9a2bd9fe2d7fd6 ...
 - 《Apache kafka实战》读书笔记-kafka集群监控工具
		
<Apache kafka实战>读书笔记-kafka集群监控工具 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 如官网所述,Kafka使用基于yammer metric ...
 - SpringBoot与Shiro整合权限管理实战
		
SpringBoot与Shiro整合权限管理实战 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] *观看本文章需要有一定SpringBoot整合经验* Shiro框架简介 Apach ...
 - 使用Apache Hudi构建大规模、事务性数据湖
		
一个近期由Hudi PMC & Uber Senior Engineering Manager Nishith Agarwal分享的Talk 关于Nishith Agarwal更详细的介绍,主 ...
 - Apache Hudi和Presto的前世今生
		
一篇由Apache Hudi PMC Bhavani Sudha Saktheeswaran和AWS Presto团队工程师Brandon Scheller分享Apache Hudi和Presto集成 ...
 
随机推荐
- threejs使用各种坑实验过程
			
第一次使用threejs到实际项目中,开始的时候心情有点小激动,毕竟是第一次嘛,然而做着做着就感受到这玩意水好深,满满的都是坑,填都填不过来.经过老板20天惨无人道的摧残,终于小有成就. 因为第一次搞 ...
 - I. 蚂蚁上树
			
蚂蚁上树(Sauteed Vermicelli with minced Pork),又名肉末粉条,是四川省及重庆市的特色传统名菜之一.因肉末贴在粉丝上,形似蚂蚁爬在树枝上而得名.这道菜具体的历史,已不 ...
 - C - Highways poj1751最小生成树
			
The island nation of Flatopia is perfectly flat. Unfortunately, Flatopia has a very poor system of p ...
 - 65535与TCP连接数的关系测试结论
			
首先说结论: .是否有关系 .有关系 对于客户端 -.对于客户端来说,只有65535,因为根据TCP四元组的sport来说,sport只有16位,所以(2^16)-1是65535.也就是最多有6553 ...
 - billu b0x2靶机渗透
			
实战渗透靶机billu b0x2 攻击kali :192.168.41.147 靶机b0x2: 192.168.41.148 起手先nmap扫了一下 扫到了四个开放的端口,有ssh,http,rpcb ...
 - Python大数据与机器学习之NumPy初体验
			
本文是Python大数据与机器学习系列文章中的第6篇,将介绍学习Python大数据与机器学习所必须的NumPy库. 通过本文系列文章您将能够学到的知识如下: 应用Python进行大数据与机器学习 应用 ...
 - 双链表【参照redis链表结构】
			
参照了Redis里面的双链表结构,可以说是完全复制粘贴,redis的双链表还是写的很通俗易懂的,没有什么花里胡哨的东西,但是redis还有个iter迭代器的结构来遍历链表.我这里就没有实现了,只是实现 ...
 - Spring Boot 之Spring data JPA简介
			
文章目录 添加依赖 添加entity bean 创建 Dao Spring Data Configuration 测试 Spring Boot 之Spring data JPA简介 JPA的全称是Ja ...
 - java1-3总结 19201421-吴志越
			
关于最近几次作业,从C语言到Java的过渡,也就是从面向过程到面向对象的过渡.其中,一共有三次作业,前俩次可能更加偏向于过程的设计,利用C语言的想法就可以完成,但是,从需要使用类的开始,就逐渐向对象偏 ...
 - Linux发送邮件命令mail,mutt
			
邮件常常是Linux下监控报警手段之一.Linux下的mail命令可以方便,快速的完成发送邮件. 1. Linux发邮件2种常见客户端命令 1.1 mail命令(推荐) 语法: 无邮件正文 mail ...