数据湖(四):Hudi 与 Spark 整合

使用Spark操作Hudi表详细教程_spark sql操作hudi,2024年最新大数据开发小程序开发实例 【代码】使用Spark操作Hudi表详细教程_spark sql操作hudi,2024年最新大数据开发小程序开发实例。 阅读详情

Hudi 与 Spark 整合

一、向 Hudi 插入数据

默认 Spark 操作 Hudi 使用表类型为 Copy On Write 模式。Hudi 与 Spark 整合时有很多参数配置,可以参照https://hudi.apache.org/docs/configurations.html配置项来查询,此外,整合时有几个需要注意的点,如下:

  • Hudi 这里使用的是 0.8.0 版本,其对应使用的 Spark 版本是 2.4.3+版本

  • Spark2.4.8 使用的 Scala 版本是 2.12 版本,虽然 2.11 也是支持的,建议使用 2.12。

  • maven 导入包中需要保证 httpclient、httpcore 版本与集群中的 Hadoop 使用的版本一致,不然会导致通信有问题。检查 Hadoop 使用以上两个包的版本路径为:$HADOOP_HOME/share/hadoop/common/lib。

  • 在编写代码过程中,指定数据写入到 HDFS 路径时直接写“/xxdir”不要写“hdfs://mycluster/xxdir”,后期会报错“java.lang.IllegalArgumentException: Not in marker dir. Marker Path=hdfs://mycluster/hudi_data/.hoodie.temp/2022xxxxxxxxxx/default/c4b854e7-51d3-4a14-9b7e-54e2e88a9701-0_0-22-22_20220509164730.parquet.marker.CREATE, Expected Marker Root=/hudi_data/.hoodie/.temp/2022xxxxxxxxxx”,可以将对应的 hdfs-site.xml、core-site.xml 放在 resources 目录下,直接会找 HDFS 路径。

1、创建项目,修改 pom.xml 为如下内容

<properties>  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  <scala.version>2.12.14</scala.version>  <spark.version>2.4.8</spark.version></properties>
<dependencies>  <!-- 指定Scala版本,这里使用2.12版本 -->  <dependency>    <groupId>org.scala-lang</groupId>    <artifactId>scala-library</artifactId>    <version>${scala.version}</version>  </dependency>  <!-- 指定HttpClient版本为4.5.2,与Hadoop集群中的版本保持一致($HADOOP_HOME/share/hadoop/common/lib/httpcore-4.4.4.jar),不然通信报错 -->  <dependency>      <groupId>org.apache.httpcomponents</groupId>      <artifactId>httpclient</artifactId>      <version>4.5.2</version>  </dependency>  <!-- 指定HttpCore版本为4.4.4,与Hadoop集群中的版本保持一致($HADOOP_HOME/share/hadoop/common/lib/httpclient-4.5.2.jar) ,不然通信报错-->  <dependency>      <groupId>org.apache.httpcomponents</groupId>      <artifactId>httpcore</artifactId>      <version>4.4.4</version>  </dependency>  <!-- Spark 依赖Jar 包 -->  <dependency>    <groupId>org.apache.spark</groupId>    <artifactId>spark-core_2.12</artifactId>    <version>${spark.version}</version>    <exclusions>      <exclusion>        <groupId>org.apache.httpcomponents</groupId>        <artifactId>httpclient</artifactId>      </exclusion>      <exclusion>          <groupId>org.apache.httpcomponents</groupId>          <artifactId>httpcore</artifactId>      </exclusion>    </exclusions>  </dependency>
  <dependency>    <groupId>org.apache.spark</groupId>    <artifactId>spark-sql_2.12</artifactId>    <version>${spark.version}</version>  </dependency>
  <dependency>    <groupId>org.apache.spark</groupId>    <artifactId>spark-avro_2.12</artifactId>    <version>${spark.version}</version>  </dependency>
  <!--连接Hive 需要的包,同时,读取Hudi parquet格式数据,也需要用到这个包中的parqurt相关类 -->  <dependency>    <groupId>org.apache.spark</groupId>    <artifactId>spark-hive_2.12</artifactId>    <version>${spark.version}</version>  </dependency>
  <!-- 连接Hive 驱动包-->  <dependency>    <groupId>org.apache.hive</groupId>    <artifactId>hive-jdbc</artifactId>    <version>1.2.1</version>  </dependency>
  <dependency>    <groupId>org.apache.hudi</groupId>    <artifactId>hudi-spark-bundle_2.12</artifactId>    <version>0.8.0</version>  </dependency>

</dependencies><build>  <plugins>    <!-- 在maven项目中既有java又有scala代码时配置 maven-scala-plugin 插件打包时可以将两类代码一起打包 -->    <plugin>      <groupId>org.scala-tools</groupId>      <artifactId>maven-scala-plugin</artifactId>      <version>2.15.2</version>      <executions>        <execution>          <goals>            <goal>compile</goal>            <goal>testCompile</goal>          </goals>        </execution>      </executions>    </plugin>
    <!-- maven 打jar包需要插件 -->    <plugin>      <artifactId>maven-assembly-plugin</artifactId>      <version>2.4</version>      <configuration>        <!-- 设置false后是去掉 MySpark-1.0-SNAPSHOT-jar-with-dependencies.jar 后的 “-jar-with-dependencies” -->        <!--<appendAssemblyId>false</appendAssemblyId>-->        <descriptorRefs>          <descriptorRef>jar-with-dependencies</descriptorRef>        </descriptorRefs>        <archive>          <manifest>            <mainClass>com.xxx</mainClass>          </manifest>        </archive>      </configuration>      <executions>        <execution>          <id>make-assembly</id>          <phase>package</phase>          <goals>            <goal>assembly</goal>          </goals>        </execution>      </executions>    </plugin>  </plugins></build>

复制代码

2、编写向 Hudi 插入数据代码

val session: SparkSession = SparkSession.builder().master("local").appName("insertDataToHudi")      .config("spark.serializer", "org.apache.spark.serializer.KryoSerializer")      .getOrCreate()
    //关闭日志//    session.sparkContext.setLogLevel("Error")
    //创建DataFrame    val insertDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\jsondata.json")

    //将结果保存到hudi中    insertDF.write.format("org.apache.hudi")//或者直接写hudi      //设置主键列名称      .option(DataSourceWriteOptions.RECORDKEY_FIELD_OPT_KEY,"id")      //当数据主键相同时,对比的字段,保存该字段大的数据      .option(DataSourceWriteOptions.PRECOMBINE_FIELD_OPT_KEY,"data_dt")S      //并行度设置,默认1500      .option("hoodie.insert.shuffle.parallelism","2")      .option("hoodie.upsert.shuffle.parallelism", "2")      //表名设置      .option(HoodieWriteConfig.TABLE_NAME,"person_infos")      .mode(SaveMode.Overwrite)      //注意:这里要选择hdfs路径存储,不要加上hdfs://mycluster//dir      //将hdfs 中core-site.xml 、hdfs-site.xml放在resource目录下,直接写/dir路径即可,否则会报错:java.lang.IllegalArgumentException: Not in marker dir. Marker Path=hdfs://mycluster/hudi_data/.hoodie\.temp/20220509164730/default/c4b854e7-51d3-4a14-9b7e-54e2e88a9701-0_0-22-22_20220509164730.parquet.marker.CREATE, Expected Marker Root=/hudi_data/.hoodie/.temp/20220509164730      .save("/hudi_data/person_infos")

复制代码

二、指定分区向 hudi 中插入数据

向 Hudi 中存储数据时,如果没有指定分区列,那么默认只有一个 default 分区,我们可以保存数据时指定分区列,可以在写出时指定“DataSourceWriteOptions.PARTITIONPATH_FIELD_OPT_KEY”选项来指定分区列,如果涉及到多个分区列,那么需要将多个分区列进行拼接生成新的字段,使用以上参数指定新的字段即可。

1、指定一个分区列

insertDF.write.format("org.apache.hudi")  .option(DataSourceWriteOptions.RECORDKEY_FIELD_OPT_KEY, "id")  .option(DataSourceWriteOptions.PRECOMBINE_FIELD_OPT_KEY, "data_dt")  //指定分区列  .option(DataSourceWriteOptions.PARTITIONPATH_FIELD_OPT_KEY, "loc")  .option("hoodie.insert.shuffle.parallelism", "2")  .option("hoodie.upsert.shuffle.parallelism", "2")  .option(HoodieWriteConfig.TABLE_NAME, "person_infos")  .mode(SaveMode.Overwrite)  .save("/hudi_data/person_infos")

复制代码

2、指定分区为多个列时,可以先拼接,后指定拼接字段当做分区列:

指定两个分区,需要拼接

//导入函数,拼接列import org.apache.spark.sql.functions._val endDF: DataFrame = insertDF.withColumn("partition_key", concat_ws("-", col("data_dt"), col("loc")))endDF.write.format("org.apache.hudi")  .option(DataSourceWriteOptions.RECORDKEY_FIELD_OPT_KEY, "id")  .option(DataSourceWriteOptions.PRECOMBINE_FIELD_OPT_KEY, "data_dt")  //指定分区列,这里是拼接的列  .option(DataSourceWriteOptions.PARTITIONPATH_FIELD_OPT_KEY, "partition_key")  .option("hoodie.insert.shuffle.parallelism", "2")  .option("hoodie.upsert.shuffle.parallelism", "2")  .option(HoodieWriteConfig.TABLE_NAME, "person_infos")  .mode(SaveMode. )   .save("/hudi_data/person_infos")

复制代码

三、 读取 Hudi 数据

使用 SparkSQL 读取 Hudi 中的数据,无法使用读取表方式来读取,需要指定 HDFS 对应的路径来加载,指定的路径只需要指定到*.parquet 当前路径或者上一层路径即可,路径中可以使用“*”来替代任意目录和数据。

读取数据返回的结果中除了原有的数据之外,还会携带 Hudi 对应的列数据,例如:hudi 的主键、分区、提交时间、对应的 parquet 名称。

Spark 读取 Hudi 表数据代码如下:

val session: SparkSession = SparkSession.builder().master("local").appName("queryDataFromHudi")  .getOrCreate()//读取的数据路径下如果有分区,会自动发现分区数据,需要使用 * 代替,指定到parquet格式数据上层目录即可。val frame: DataFrame = session.read.format("org.apache.hudi").load("/hudi_data/person_infos/*/*")frame.createTempView("personInfos")
//查询结果val result = session.sql(  """    | select * from personInfos  """.stripMargin)
result.show(false)

复制代码

四、更新 Hudi 数据

向 Hudi 中更新数据有如下几个特点

  • 同一个分区内,向 Hudi 中更新数据是用主键来判断数据是否需要更新的,这里判断的是相同分区内是否有相同主键,不同分区内允许有相同主键。

  • 更新数据时,如果原来数据有分区,一定要指定分区,不然就相当于是向相同表目录下插入数据,会生成对应的“default”分区。

  • 向 Hudi 中更新数据时,与向 Hudi 中插入数据一样,但是写入的模式需要指定成“Append”,如果指定成“overwrite”,那么就是全覆盖了。建议使用时一直使用“Append”模式即可。

  • 当更新完成之后,再一次从 Hudi 中查询数据时,会看到 Hudi 提交的时间字段为最新的时间。

这里将原有的三条数据改成如下三条数据:

#修改之前{"id":1,"name":"zs1","age":18,"loc":"beijing","data_dt":"20210709"}{"id":2,"name":"zs2","age":19,"loc":"shanghai","data_dt":"20210709"}{"id":3,"name":"zs3","age":20,"loc":"beijing","data_dt":"20210709"}
#修改之后{"id":1,"name":"ls1","age":40,"loc":"beijing","data_dt":"20210709"} --更新数据{"id":2,"name":"ls2","age":50,"loc":"shanghai","data_dt":"20210710"} --更新数据{"id":3,"name":"ls3","age":60,"loc":"ttt","data_dt":"20210711"}  --相当于是新增数据

复制代码

更新 Hudi 数据代码如下:

val session: SparkSession = SparkSession.builder().master("local").appName("updataDataToHudi")  .config("spark.serializer", "org.apache.spark.serializer.KryoSerializer")  .getOrCreate()
//读取修改数据val updateDataDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\updatedata.json")
//向Hudi 更新数据updateDataDF.write.format("org.apache.hudi") //或者直接写hudi  .option(DataSourceWriteOptions.RECORDKEY_FIELD_OPT_KEY, "id")  .option(DataSourceWriteOptions.PRECOMBINE_FIELD_OPT_KEY, "data_dt")  .option(DataSourceWriteOptions.PARTITIONPATH_FIELD_OPT_KEY,"loc")  .option("hoodie.insert.shuffle.parallelism", "2")  .option("hoodie.upsert.shuffle.parallelism", "2")  .option(HoodieWriteConfig.TABLE_NAME, "person_infos")  .mode(SaveMode.Append)  .save("/hudi_data/person_infos")

//查询数据val frame: DataFrame = session.read.format("org.apache.hudi").load("/hudi_data/person_infos/*/*")frame.createTempView("personInfos")//查询结果val result = session.sql(  """    | select * from personInfos  """.stripMargin)result.show(false)

复制代码

五、 增量查询 Hudi 数据

Hudi 可以根据我们传入的时间戳查询此时间戳之后的数据,这就是增量查询,需要注意的是增量查询必须通过以下方式在 Spark 中指定一个时间戳才能正常查询:

option(DataSourceReadOptions.BEGIN_INSTANTTIME_OPT_KEY,timestamp)

例如:原始数据如下:

我们可以查询“20210709220335”之后的数据,查询结果如下:

代码如下:

val session: SparkSession = SparkSession.builder().master("local").appName("updataDataToHudi")  .getOrCreate()
//关闭日志session.sparkContext.setLogLevel("Error")
//导入隐式转换import session.implicits._
//查询全量数据,查询对应的提交时间,找出倒数第二个时间val basePath = "/hudi_data/person_infos"session.read.format("hudi").load(basePath+"/*/*").createTempView("personInfos")
val df: DataFrame = session.sql("select distinct(_hoodie_commit_time) as commit_time from personInfos order by commit_time desc")//这里获取由大到小排序的第二个值val dt: String = df.map(row=>{row.getString(0)}).collect()(1)
//增量查询val result:DataFrame = session.read.format("hudi")/**  * 指定数据查询方式,有以下三种:  * val QUERY_TYPE_SNAPSHOT_OPT_VAL = "snapshot"    -- 获取最新所有数据 , 默认  * val QUERY_TYPE_INCREMENTAL_OPT_VAL = "incremental"  --获取指定时间戳后的变化数据  * val QUERY_TYPE_READ_OPTIMIZED_OPT_VAL = "read_optimized"  -- 只查询Base文件中的数据  *  * 1) Snapshot mode (obtain latest view, based on row & columnar data)  * 2) incremental mode (new data since an instantTime)  * 3) Read Optimized mode (obtain latest view, based on columnar data)  *  * Default: snapshot  */  .option(DataSourceReadOptions.QUERY_TYPE_OPT_KEY,DataSourceReadOptions.QUERY_TYPE_INCREMENTAL_OPT_VAL)  //必须指定一个开始查询的时间,不指定报错  .option(DataSourceReadOptions.BEGIN_INSTANTTIME_OPT_KEY,dt)  .load(basePath+"/*/*")
result.show(false)

复制代码

六、指定时间范围查询 Hudi 数据

Hudi 还可以通过指定开始时间和结束时间来查询时间范围内的数据。如果想要查询最早的时间点到某个结束时刻的数据,开始时间可以指定成“000”。

1、向原有 Hudi 表“person_infos”中插入两次数据

目前 hudi 表中的数据如下:

先执行两次新的数据插入,两次插入数据之间的间隔时间至少为 1 分钟,两次插入数据代码如下:

//以下代码分两次向 HDFS /hudi_data/person_infos 路径中插入数据,两次运行至少1分钟以上val session: SparkSession = SparkSession.builder().master("local").appName("PointTimeQueryHudi")  .config("spark.serializer", "org.apache.spark.serializer.KryoSerializer")  .getOrCreate()
//读取第一个文件,向Hudi中插入数据val df1: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\appendData1.json")val df2: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\appendData2.json")
//向Hudi中插入数据df2.write.format("hudi")  .option(DataSourceWriteOptions.RECORDKEY_FIELD_OPT_KEY, "id")  .option(DataSourceWriteOptions.PRECOMBINE_FIELD_OPT_KEY, "data_dt")  .option(DataSourceWriteOptions.PARTITIONPATH_FIELD_OPT_KEY, "loc")  .option("hoodie.insert.shuffle.parallelism", "2")  .option("hoodie.upsert.shuffle.parallelism", "2")  .option(HoodieWriteConfig.TABLE_NAME, "person_infos")  .mode(SaveMode.Append)  .save("/hudi_data/person_infos")
import org.apache.spark.sql.functions._//查询数据session.read.format("hudi").load("/hudi_data/person_infos/*/*")  .orderBy(col("_hoodie_commit_time"))  .show(100,false)

复制代码

此时,数据如下:

2、指定时间段查询 Hudi 中的数据

代码如下:

val session: SparkSession = SparkSession.builder().master("local").appName("PointTimeQueryHudi")      .config("spark.serializer", "org.apache.spark.serializer.KryoSerializer")      .getOrCreate()    //指定时间段,查询hudi中的数据//    val beginTime = "000"    val beginTime = "20210710002148"    val endTime = "20210710002533"
    val result: DataFrame = session.read.format("hudi")      //指定增量查询      .option(DataSourceReadOptions.QUERY_TYPE_OPT_KEY, DataSourceReadOptions.QUERY_TYPE_INCREMENTAL_OPT_VAL)      //指定查询开始时间(不包含),“000”指定为最早时间      .option(DataSourceReadOptions.BEGIN_INSTANTTIME_OPT_KEY, beginTime)      //指定查询结束时间(包含)      .option(DataSourceReadOptions.END_INSTANTTIME_OPT_KEY, endTime)      .load("/hudi_data/person_infos/*/*")
    result.createTempView("temp")    session.sql(      """        | select * from temp order by _hoodie_commit_time      """.stripMargin).show(100,false)

复制代码

开始时间为“000”,相当于是从头开始查询到 endTime 的数据:

开始时间为“20210710002148”:

七、删除 Hudi 数据

我们准备对应的主键及分区的数据,将 Hudi 中对应的主键及分区的数据进行删除,在删除 Hudi 中的数据时,需要指定 option(OPERATION_OPT_KEY,"delete")配置项,并且写入模式只能是 Append,不支持其他写入模式,另外,设置下删除执行的并行度,默认为 1500 个,这里可以设置成 2 个。

原始数据如下:

准备要删除的数据如下:

{"id":11,"loc":"beijing"}{"id":12,"loc":"beijing"}{"id":13,"loc":"beijing"}{"id":14,"loc":"shenzhen"}{"id":15,"loc":"tianjian"}  --此条数据对应的主键一致,但是分区不一致,不能在Hudi中删除

复制代码

编写代码如下:

val session: SparkSession = SparkSession.builder().master("local").appName("DeleteHudiData")  .config("spark.serializer","org.apache.spark.serializer.KryoSerializer")  .getOrCreate()
//读取需要删除的数据,只需要准备对应的主键及分区即可,字段保持与Hudi中需要删除的字段名称一致即可//读取的文件中准备了一个主键在Hudi中存在但是分区不再Hudi中存在的数据,此主键数据在Hudi中不能被删除,需要分区和主键字段都匹配才能删除val deleteData: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\deleteData.json")
//将删除的数据插入到Hudi中deleteData.write.format("hudi")  //指定操作模式为delete  .option(DataSourceWriteOptions.OPERATION_OPT_KEY,"delete")  //指定主键  .option(DataSourceWriteOptions.RECORDKEY_FIELD_OPT_KEY,"id")  //指定分区字段  .option(DataSourceWriteOptions.PARTITIONPATH_FIELD_OPT_KEY,"loc")  //指定表名,这里的表明需要与之前指定的表名保持一致  .option(HoodieWriteConfig.TABLE_NAME,"person_infos")  //设置删除并行度设置,默认1500并行度  .option("hoodie.delete.shuffle.parallelism", "2")  .mode(SaveMode.Append)  .save("/hudi_data/person_infos")
//执行完成之后,查询结果import org.apache.spark.sql.functions._session.read.format("hudi").load("/hudi_data/person_infos/*/*")  .orderBy(col("_hoodie_commit_time")).show(100,false)

复制代码

结果如下:

八、更新 Hudi 某个分区数据

如果我们想要更新 Hudi 某个分区的数据,其他分区数据正常使用,那么可以通过配置 option(DataSourceWriteOptions.OPERATION_OPT_KEY,"insert_overwrite")选项,该选项“insert_overwrite”可以直接在元数据层面上操作,直接将写入某分区的新数据替换到该分区内,原有数据会在一定时间内删除,相比 upsert 更新 Hudi 速度要快。

1、删除 person_infos 对应的目录,重新插入数据,代码如下

val session: SparkSession = SparkSession.builder().master("local").appName("InsertOverWrite")  .config("spark.serializer","org.apache.spark.serializer.KryoSerializer")  .getOrCreate()
//创建DataFrameval insertDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\jsondata.json")insertDF.write.format("org.apache.hudi")  .option(DataSourceWriteOptions.RECORDKEY_FIELD_OPT_KEY, "id")  .option(DataSourceWriteOptions.PRECOMBINE_FIELD_OPT_KEY, "data_dt")  .option(DataSourceWriteOptions.PARTITIONPATH_FIELD_OPT_KEY, "loc")  .option("hoodie.insert.shuffle.parallelism", "2")  .option("hoodie.upsert.shuffle.parallelism", "2")  .option(HoodieWriteConfig.TABLE_NAME, "person_infos")  .mode(SaveMode.Append)  .save("/hudi_data/person_infos")
//写入完成之后,查询hudi 数据:val person_infos: DataFrame = session.read.format("hudi").load("/hudi_data/person_infos/*/*")person_infos.show(100,false)

复制代码

2、读取更新分区数据,插入到 Hudi preson_infos 表中

读取数据如下:

{"id":1,"name":"s1","age":1,"loc":"beijing","data_dt":"20210710"}{"id":100,"name":"s2","age":2,"loc":"beijing","data_dt":"20210710"}{"id":200,"name":"s3","age":3,"loc":"beijing","data_dt":"20210710"}{"id":8,"name":"w1","age":4,"loc":"chongqing","data_dt":"20210710"}{"id":300,"name":"w2","age":5,"loc":"chongqing","data_dt":"20210710"}

复制代码

代码如下:

val session: SparkSession = SparkSession.builder().master("local").appName("InsertOverWrite")      .config("spark.serializer","org.apache.spark.serializer.KryoSerializer")      .getOrCreate()
    //读取需要替换的数据,将beijing分区数据替换成2条,将chognqing分区数据替换成1条    val overWritePartitionData: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\overWrite.json")
    //写入hudi表person_infos,替换分区    overWritePartitionData.write.format("hudi")      .option(DataSourceWriteOptions.OPERATION_OPT_KEY,"insert_overwrite")      //设置主键列名称      .option(DataSourceWriteOptions.RECORDKEY_FIELD_OPT_KEY, "id")      //当数据主键相同时,对比的字段,保存该字段大的数据      .option(DataSourceWriteOptions.PRECOMBINE_FIELD_OPT_KEY, "data_dt")      //指定分区列      .option(DataSourceWriteOptions.PARTITIONPATH_FIELD_OPT_KEY, "loc")      //并行度设置      .option("hoodie.insert.shuffle.parallelism", "2")      .option("hoodie.upsert.shuffle.parallelism", "2")      //表名设置      .option(HoodieWriteConfig.TABLE_NAME, "person_infos")      .mode(SaveMode.Append)      .save("/hudi_data/person_infos")
    //写入完成之后,查询hudi 数据:    val person_infos: DataFrame = session.read.format("hudi").load("/hudi_data/person_infos/*/*")    person_infos.show(100,false)

复制代码

九、覆盖 Hudi 整个表数据

如果我们想要替换 Hudi 整个表数据,可以在向 Hudi 表写入数据时指定配置 option(DataSourceWriteOptions.OPERATION_OPT_KEY,"insert_overwrite_table")选项,该选项“insert_overwrite_table”可以直接在元数据层面上操作,直接将数据写入表,原有数据会在一定时间内删除,相比删除原有数据再插入更方便。

1、删除 Hudi 表 person_infos 对应的 HDFS 路径,重新插入数据

val session: SparkSession = SparkSession.builder().master("local").appName("InsertOverWrite")      .config("spark.serializer","org.apache.spark.serializer.KryoSerializer")      .getOrCreate()
    //创建DataFrame    val insertDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\jsondata.json")    insertDF.write.format("org.apache.hudi")      .option(DataSourceWriteOptions.RECORDKEY_FIELD_OPT_KEY, "id")      .option(DataSourceWriteOptions.PRECOMBINE_FIELD_OPT_KEY, "data_dt")      .option(DataSourceWriteOptions.PARTITIONPATH_FIELD_OPT_KEY, "loc")      .option("hoodie.insert.shuffle.parallelism", "2")      .option("hoodie.upsert.shuffle.parallelism", "2")      .option(HoodieWriteConfig.TABLE_NAME, "person_infos")      .mode(SaveMode.Append)      .save("/hudi_data/person_infos")
    //写入完成之后,查询hudi 数据:    val person_infos: DataFrame = session.read.format("hudi").load("/hudi_data/person_infos/*/*")    person_infos.show(100,false)

复制代码

2、读取新数据,覆盖原有 Hudi 表数据

覆盖更新的数据如下:

{"id":1,"name":"s1","age":1,"loc":"beijing","data_dt":"20210710"}{"id":100,"name":"s2","age":2,"loc":"beijing","data_dt":"20210710"}{"id":200,"name":"s3","age":3,"loc":"beijing","data_dt":"20210710"}{"id":8,"name":"w1","age":4,"loc":"chongqing","data_dt":"20210710"}{"id":300,"name":"w2","age":5,"loc":"chongqing","data_dt":"20210710"}

复制代码

代码如下:

val session: SparkSession = SparkSession.builder().master("local").appName("InsertOverWrite")      .config("spark.serializer","org.apache.spark.serializer.KryoSerializer")      .getOrCreate()
    //读取需要替换的数据,覆盖原有表所有数据    val overWritePartitionData: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\overWrite.json")
    //写入hudi表person_infos,替换分区    overWritePartitionData.write.format("hudi")      .option(DataSourceWriteOptions.OPERATION_OPT_KEY,"insert_overwrite_table")      //设置主键列名称      .option(DataSourceWriteOptions.RECORDKEY_FIELD_OPT_KEY, "id")      //当数据主键相同时,对比的字段,保存该字段大的数据      .option(DataSourceWriteOptions.PRECOMBINE_FIELD_OPT_KEY, "data_dt")      //指定分区列      .option(DataSourceWriteOptions.PARTITIONPATH_FIELD_OPT_KEY, "loc")      //并行度设置      .option("hoodie.insert.shuffle.parallelism", "2")      .option("hoodie.upsert.shuffle.parallelism", "2")      //表名设置      .option(HoodieWriteConfig.TABLE_NAME, "person_infos")      .mode(SaveMode.Append)      .save("/hudi_data/person_infos")
    //写入完成之后,查询hudi 数据:    val person_infos: DataFrame = session.read.format("hudi").load("/hudi_data/person_infos/*/*")    person_infos.show(100,false)

复制代码

十、Spark 操作 Hudi Merge On Read 模式

默认 Spark 操作 Hudi 使用 Copy On Write 模式,也可以使用 Merge On Read 模式,通过代码中国配置如下配置来指定:

option(DataSourceWriteOptions.TABLE_TYPE_OPT_KEY,DataSourceWriteOptions.MOR_TABLE_TYPE_OPT_VAL)

代码操作如下:

  • 删除原有 person_infos 对应的 HDFS 路径

  • 读取数据向 Hudi 表 person_info 中插入数据

读取的数据如下:

{"id":1,"name":"zs1","age":18,"loc":"beijing","data_dt":"20210709"}{"id":2,"name":"zs2","age":19,"loc":"shanghai","data_dt":"20210709"}{"id":3,"name":"zs3","age":20,"loc":"beijing","data_dt":"20210709"}{"id":4,"name":"zs4","age":21,"loc":"tianjin","data_dt":"20210709"}{"id":5,"name":"zs5","age":22,"loc":"shenzhen","data_dt":"20210709"}{"id":6,"name":"zs6","age":23,"loc":"hainai","data_dt":"20210709"}{"id":7,"name":"zs7","age":24,"loc":"beijing","data_dt":"20210709"}{"id":8,"name":"zs8","age":25,"loc":"chongqing","data_dt":"20210709"}{"id":9,"name":"zs9","age":26,"loc":"shandong","data_dt":"20210709"}{"id":10,"name":"zs10","age":27,"loc":"hunan","data_dt":"20210709"}

复制代码

代码如下:

//1.读取json格式数据val insertDf: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\jsondata.json")
//2.将结果使用Merge on Read 模式写入到Hudi中,并设置分区insertDf.write.format("hudi")  //设置表模式为 mor  .option(DataSourceWriteOptions.TABLE_TYPE_OPT_KEY,DataSourceWriteOptions.MOR_TABLE_TYPE_OPT_VAL)  .option(DataSourceWriteOptions.RECORDKEY_FIELD_OPT_KEY,"id")  .option(DataSourceWriteOptions.PARTITIONPATH_FIELD_OPT_KEY,"loc")  .option(DataSourceWriteOptions.PRECOMBINE_FIELD_OPT_KEY,"data_dt")  //并行度设置  .option("hoodie.insert.shuffle.parallelism", "2")  .option("hoodie.upsert.shuffle.parallelism", "2")  //表名设置  .option(HoodieWriteConfig.TABLE_NAME, "person_infos")  .mode(SaveMode.Append)  .save("/hudi_data/person_infos")

复制代码

  • 更新 Hudi 表 person_info 数据

这里更新“beijing”、“shanghai”、“ttt”分区数据,更新数据如下:

{"id":1,"name":"ls1","age":40,"loc":"beijing","data_dt":"20210709"}{"id":2,"name":"ls2","age":50,"loc":"shanghai","data_dt":"20210710"}{"id":3,"name":"ls3","age":60,"loc":"ttt","data_dt":"20210711"}

复制代码

代码如下:

//3.读取更新数据,并执行插入更新val updateDf: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\updatedata.json")
updateDf.write.format("hudi")  //设置表模式为 mor  .option(DataSourceWriteOptions.TABLE_TYPE_OPT_KEY,DataSourceWriteOptions.MOR_TABLE_TYPE_OPT_VAL)  .option(DataSourceWriteOptions.RECORDKEY_FIELD_OPT_KEY,"id")  .option(DataSourceWriteOptions.PARTITIONPATH_FIELD_OPT_KEY,"loc")  .option(DataSourceWriteOptions.PRECOMBINE_FIELD_OPT_KEY,"data_dt")  //并行度设置  .option("hoodie.insert.shuffle.parallelism", "2")  .option("hoodie.upsert.shuffle.parallelism", "2")  //表名设置  .option(HoodieWriteConfig.TABLE_NAME, "person_infos")  .mode(SaveMode.Append)  .save("/hudi_data/person_infos")

复制代码

  • 增量查询 Hudi 表中的数据

Snapshot 模式查询,这种模式对于 COW 或者 MOR 模式都是查询到当前时刻全量的数据,如果有更新,那么就是更新之后全量的数据:

//4.使用不同模式查询 MOR 表中的数据/**  * 指定数据查询方式,有以下三种:  * val QUERY_TYPE_SNAPSHOT_OPT_VAL = "snapshot"    -- 获取最新所有数据 , 默认  * val QUERY_TYPE_INCREMENTAL_OPT_VAL = "incremental"  --获取指定时间戳后的变化数据  * val QUERY_TYPE_READ_OPTIMIZED_OPT_VAL = "read_optimized"  -- 只查询Base文件中的数据  *  * 1) Snapshot mode (obtain latest view, based on row & columnar data)  * 2) incremental mode (new data since an instantTime)  * 3) Read Optimized mode (obtain latest view, based on columnar data)  *  * Default: snapshot  *///4.1 Snapshot 模式查询session.read.format("hudi")  .option(DataSourceReadOptions.QUERY_TYPE_OPT_KEY,DataSourceReadOptions.QUERY_TYPE_SNAPSHOT_OPT_VAL)  .load("/hudi_data/person_infos/*/*")  .show(100,false)

复制代码

incremental 模式查询,这种模式需要指定一个时间戳,查询指定时间戳之后的新增数据:

//4.2 incremental 模式查询,查询指定时间戳后的数据session.read.format("hudi")  .option(DataSourceReadOptions.QUERY_TYPE_OPT_KEY,DataSourceReadOptions.QUERY_TYPE_INCREMENTAL_OPT_VAL)  //必须指定一个开始查询的时间,不指定报错  .option(DataSourceReadOptions.BEGIN_INSTANTTIME_OPT_KEY,"20210710171240")  .load("/hudi_data/person_infos/*/*")  .show(100,false)

复制代码

Read Optimized 模式查询,这种模式只查询 Base 中的数据,不会查询 MOR 中 Log 文件中的数据,代码如下:

//4.3 Read Optimized 模式查询,查询Base中的数据,不会查询log中的数据session.read.format("hudi")  .option(DataSourceReadOptions.QUERY_TYPE_OPT_KEY,DataSourceReadOptions.QUERY_TYPE_READ_OPTIMIZED_OPT_VAL)  .load("/hudi_data/person_infos/*/*")  .show(100,false)

复制代码

十一、测试 COW 模式 parquet 文件删除与 MOR 模式 Parquet 文件与 log 文件 Compact

COW 默认情况下,每次更新数据 Commit 都会基于之前 parquet 文件生成一个新的 Parquet Base 文件数据,默认历史 parquet 文件数为 10,当超过 10 个后会自动删除旧的版本,可以通过参数“hoodie.cleaner.commits.retained”来控制保留的 FileID 版本文件数,默认是 10。测试代码如下:

val session: SparkSession = SparkSession.builder().master("local").appName("insertDataToHudi")      .config("spark.serializer", "org.apache.spark.serializer.KryoSerializer")      .getOrCreate()    //创建DataFrame    val insertDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\test\\insertdata1.json")//    val insertDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\test\\insertdata2.json")//    val insertDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\test\\insertdata3.json")//    val insertDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\test\\insertdata4.json")//    val insertDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\test\\insertdata5.json")//    val insertDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\test\\insertdata6.json")//    val insertDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\test\\insertdata7.json")//    val insertDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\test\\insertdata8.json")//    val insertDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\test\\insertdata9.json")//    val insertDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\test\\insertdata10.json")
    insertDF.write.format("org.apache.hudi")      //设置cow模式      .option(DataSourceWriteOptions.TABLE_TYPE_OPT_KEY,DataSourceWriteOptions.COW_TABLE_TYPE_OPT_VAL)      //根据commit提交次数计算保留多少个fileID版本文件,默认10。      .option("hoodie.cleaner.commits.retained","3")      //设置主键列名称      .option(DataSourceWriteOptions.RECORDKEY_FIELD_OPT_KEY, "id")      //当数据主键相同时,对比的字段,保存该字段大的数据      .option(DataSourceWriteOptions.PRECOMBINE_FIELD_OPT_KEY, "data_dt")      //并行度设置,默认1500并行度      .option("hoodie.insert.shuffle.parallelism", "2")      .option("hoodie.upsert.shuffle.parallelism", "2")      //表名设置      .option(HoodieWriteConfig.TABLE_NAME, "person_infos")      .mode(SaveMode.Append)      .save("/hudi_data/test_person")
    //查询结果数据    session.read.format("hudi")      //全量读取      .option(DataSourceReadOptions.QUERY_TYPE_OPT_KEY,DataSourceReadOptions.QUERY_TYPE_SNAPSHOT_OPT_VAL)      .load("/hudi_data/test_person/*/*").show()

复制代码

测试注意:每次运行代码,读取新的一个数据文件,并查看 Hudi 表对应的 HDFS 路径,每次读取都会生成一个新的 Parquet 文件,当达到指定的 3 个历史版本时(不包含最新 Parquet 文件),再插入数据生成新的 Parquet 文件时,一致会将之前的旧版本删除,保存 4 个文件。

MOR 模式下,如果有新增数据会直接写入 Base Parquet 文件,这个 Parquet 文件个数的控制也是由“hoodie.cleaner.commits.retained”控制,默认为 10。当对应的每个 FlieSlice(Base Parquet 文件+log Avro 文件)中有数据更新时,会写入对应的 log Avro 文件,那么这个文件何时与 Base Parquet 文件进行合并,这个是由参数“hoodie.compact.inline.max.delta.commits”决定的,这个参数意思是在提交多少次 commit 后触发压缩策略,默认是 5,也就是当前 FlieSlice 中如果有 5 次数据更新就会两者合并生成全量的数据,当前 FlieSlice 还是这个 FileSlice 名称,只不过对应的 parquet 文件中是全量数据,再有更新数据还是会写入当前 FileSlice 对应的 log 日志文件中。使“hoodie.compact.inline.max.delta.commits”参数起作用,默认必须开启“hoodie.compact.inline”,此值代表是否完成提交数据后进行压缩,默认是 false。

测试代码如下:

#注意代码中设置参数如下://根据commit提交次数计算保留多少个fileID版本文件,默认10。.option("hoodie.cleaner.commits.retained","3")//默认false:是否在一个事务完成后内联执行压缩操作.option("hoodie.compact.inline","true")//设置提交多少次后触发压缩策略,默认5.option("hoodie.compact.inline.max.delta.commits","2")
#完整代码如下:val session: SparkSession = SparkSession.builder().master("local").appName("insertDataToHudi")      .config("spark.serializer", "org.apache.spark.serializer.KryoSerializer")      .getOrCreate()    //创建DataFrame ,新增//    val insertDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\test\\insertdata1.json")
    //创建DataFrame ,更新    val insertDF: DataFrame = session.read.json("file:///D:\\2022IDEA_space\\SparkOperateHudi\\data\\test\\update11.json")
    insertDF.write.format("org.apache.hudi") //或者直接写hudi      .option(DataSourceWriteOptions.TABLE_TYPE_OPT_KEY,DataSourceWriteOptions.MOR_TABLE_TYPE_OPT_VAL)      //根据commit提交次数计算保留多少个fileID版本文件,默认10。      .option("hoodie.cleaner.commits.retained","3")      //默认false:是否在一个事务完成后内联执行压缩操作      .option("hoodie.compact.inline","true")      //设置提交多少次后触发压缩策略,默认5      .option("hoodie.compact.inline.max.delta.commits","2")      //设置主键列名称      .option(DataSourceWriteOptions.RECORDKEY_FIELD_OPT_KEY, "id")      //当数据主键相同时,对比的字段,保存该字段大的数据      .option(DataSourceWriteOptions.PRECOMBINE_FIELD_OPT_KEY, "data_dt")      //并行度设置,默认1500并行度      .option("hoodie.insert.shuffle.parallelism", "2")      .option("hoodie.upsert.shuffle.parallelism", "2")      //表名设置      .option(HoodieWriteConfig.TABLE_NAME, "person_infos")      .mode(SaveMode.Append)      .save("/hudi_data/test_person")
    //查询结果数据    session.read.format("hudi")      //全量读取      .option(DataSourceReadOptions.QUERY_TYPE_OPT_KEY,DataSourceReadOptions.QUERY_TYPE_SNAPSHOT_OPT_VAL)      .load("/hudi_data/test_person/*/*").show()

复制代码

第一次运行插入数据,commit,路径对应数据目录如下:

第一次运行更新数据,commit,路径对应数据目录如下:

第二次运行更新数据,commit,路径对应的数据目录如下:

第三次运行更新数据,commit,路径对应的数据目录如下:

第四次运行更新数据,commit,路径对应的数据目录如下:

第五次运行更新数据,commit,路径对应的目录数据如下:

使用Spark操作Hudi表详细教程_spark读取hudi 【代码】使用Spark操作Hudi表详细教程_spark读取hudi。 阅读详情

相关推荐

数据湖浅析(以hudi为例)

数据湖浅析

weixin_45626756的博客 3978

Hudi入门:Spark集成使用Hudi

官网部署文档翻译

主要写大数据集群一线运维和学习工作经验 1743

数据湖技术之Hudi:核心原理、Spark & Hive 集成、COW 与 MOR 模式详解

摘要 Hudi是Apache开源的数据湖解决方案,支持HDFS上大规模分析数据集的更新、增量消费等操作,旨在降低数据延迟。相比传统数据仓库,数据湖能存储多源异构数据,采用"读时模式"提供更高灵活性。Hudi通过批流一体存储解决了Kappa架构的痛点(如Kafka存储限制),实现高效OLAP查询。其核心特性包括:基于时间线的数据版本管理(支持6种操作类型)、MVCC设计、文件分组优化、布隆过滤器索引等,提供upsert、原子提交、快照隔离等能力,适用于实时数仓场景。Hudi轻量级集成Spa

yangwei2048的博客 1185

spark集成hudi

spark集成hudi

m0_65077254的博客 594

数据湖架构开发-Hudi入门教程

当下大数据技术在工业环境中应用,主要构建离线数仓和实时数仓,进行大规模数据管理和分析。为了更好管理数据,出现数据湖DataLake:用来存储大量的原始数据。当下最流行数据湖框架Apache Hudi,使用HDFS存储数据,集成Spark、Flink、Hive等分析数据,逐步在大中型公司中使用,构建湖仓一体化架构。为了让大家更好学习使用Hudi,基于0.9.0版本,推出本套课程,从数据湖概念,到集成Spark和Flink,最后整合实际需求案例,由0到1深入浅出剖析Hudi使用,整个课程分为:基础入门篇、应用进

Aa112233aA1的博客 2855

数据湖(四):Hudi与Spark整合

默认Spark操作Hudi使用表类型为Copy On Write模式。Hudi与Spark整合时有很多参数配置,可以参照https://hudi.apache.org/docs/configurations.html配置项来查询,此外,整合时有几个需要注意的点,如下: Hudi这里使用的是0.8.0版本,其对应使用的Spark版本是2.4.3+版本 Spark2.4.8使用的Scala版本是2.12版本,虽然2.11也是支持的,建议使用2.12

Lansonli(蓝深李)的博客 3264

【Hudi】数据湖(四):Hudi与Spark整合

默认Spark操作Hudi使用表类型为Copy On Write模式。Hudi与Spark整合时有很多参数配置,可以参照https://hudi.apache.org/docs/configurations.html配置项来查询,此外,整合时有几个需要注意的点,如下: 2、编写向Hudi插入数据代码 向Hudi中存储数据时,如果没有指定分区列,那么默认只有一个default分区,我们可以保存数据时指定分区列,可以在写出时指定“DataSourceWriteOptions.PARTITIONPATH_FIE

菜鸟叔叔的博客 2591

FPGA实战:手把手教你用Verilog驱动4.3寸RGB触摸屏(附完整代码)

本文详细介绍了如何使用Verilog语言在FPGA上驱动4.3寸RGB触摸屏,包括时序设计、信号交互原理及完整代码实现。通过双计数器引擎设计和参数化模块,开发者可以高效构建800*480分辨率的显示驱动,并解决工程实践中的信号完整性和时序收敛问题。

rice5的博客 701

大数据Hadoop之——Apache Hudi 数据湖实战操作(Spark,Flink与Hudi整合)

简称Hudi,是一个流式数据湖平台,支持对海量数据快速更新,内置表格式,支持事务的存储层、 一系列表服务、数据服务(开箱即用的摄取工具)以及完善的运维监控工具,它可以以极低的延迟将数据快速存储到HDFS或云存储(S3)的工具,最主要的特点支持记录级别的插入更新(Upsert)和删除,同时还支持增量查询。大数据Hadoop之——新一代流式数据湖平台 Apache Hudi。

匠人精神,持之以恒! 2703

实验五 循环结构第1关:斐波那契数列第2关:统计一段英文语句中的单词数和句子数第3关:密码判断第4关:通过数列求自然常数e第5关:双倍余额递减法计算固定资产折旧第6关:销售统计第7关:投资年限计算

第1关:斐波那契数列第2关:统计一段英文语句中的单词数和句子数第3关:密码判断 第4关:通过数列求自然常数e 第5关:双倍余额递减法计算固定资产折旧

pro_daily的博客 8168

MATPOWER下载安装教程

将MATPOWER文件复制在C:\Program Files (x86)\MATLAB\R2016b\bin目录下。原来的窗口中多了很多文件路径,点击【保存】,再点击【关闭】选择所要添加的MATPOWER文件夹,点击【选择文件夹】二、将MATPOWER放在bin目录下。找到菜单栏的【设置路径】点击【添加并包含子文件】一、从官网下载后解压。

weixin_47365903的博客 2万+

数据湖技术之Hudi 集成 Spark

数据湖框架Hudi,从诞生之初支持Spark进行操作,后期支持Flink,接下来先看看与Spark整合使用,并且在0.9.0版本中,提供SparkSQL支持,编写DDL和DML操作数据。Hudi数据湖框架,开始与Spark分析引擎框架整合,通过Spark保存数据到Hudi表,使用Spark加载Hudi表数据进行分析,不仅支持批处理和流计算,还可以集成Hive进行数据分析,安装大数据其他框架:MySQL、Hive、Zookeeper及Kafka,便于案例集成整合使用。 采用tar方式安装MySQL数据库,具体

数字人生 2281

NDIAurora 6D、5D传感器裸线焊接与烧录 6D传感器型号:10001742;5D传感器型号:610157

NDI Aurora 6D、5D传感器裸线焊接与烧录

yanghong2000719的博客 1201

数据湖架构Hudi(四)Hudi集成Spark案例详解

Hudi集成Spark案例详解

qq_44665283的博客 2465

Android Studio打包全攻略---从入门到精通

初出茅庐 手动打包怎么手动打包项目写完了,现在需要把应用上传到市场,问题出现—怎么把代码变成.apk(Android的可安装文件)。 1. 创建签名文件 2. 填写好签名参数 3. 生成APK 注意:签名的密码和密匙的密码注意保管,不要忘了,签名文件别泄漏了,也别搞丢了为什么要打包我最开始就有这个疑问,我们的代码不是点了下运行按钮就直接安装到手机上了吗,我们在在项目Project

我早就想,要这样载着你,翻山越岭 2万+

通俗易懂的Harris 角点检测

点击上方“3D视觉工坊”,选择“星标”干货第一时间送达Harris 角点检测 认为特征点具有局部差异性。如何描述“特征点具有局部差异性”:以每一个点为中心,取一个窗口,窗口大小为5*5...

3D视觉工坊 1473

Hudi数据湖技术之集成Spark

目录 1 环境准备 1.1 安装MySQL 5.7.31 1.2 安装Hive 2.1 1.3 安装Zookeeper 3.4.6 1.4 安装Kafka 2.4.1 2 滴滴运营分析 2.1 需求说明 2.2 环境准备 2.2.1 工具类SparkUtils 2.2.2 日期转换星期 2.3 数据ETL保存 2.3.1 开发步骤 2.3.2 加载CSV数据 2.3.3 数据ETL转换 2.3.4 保存数据至Hudi 2.3.5 Hudi 表存储结构 2.4 指标查询分析 2.4.1 开发步骤

赵广陆 1753

GibbsCAM软件二次开发:GibbsCAM软件API接口详解

API(Application Programming Interface,应用程序编程接口)是一种允许不同软件之间进行通信的接口。在GibbsCAM软件中,API提供了与外部程序交互的能力,使得开发者可以利用GibbsCAM的强大功能进行二次开发,定制化满足特定的制造需求。GibbsCAM的API主要基于.NET框架,使用C#或VB.NET等语言进行编程。在GibbsCAM软件中,通过API接口创建自定义几何形状是实现复杂零件设计和加工策略定制的关键步骤。

kkchenjj的博客 1496

【大数据开发】Hudi——Hudi快速入门

Hudi快速入门 Hudi中文文档 spark2.4版本以上才能整合Hudi 一、设计原则 流式读/写 Hudi应用于大型数据集记录流的输入和输出 Hudi通过其特殊列添加并跟踪记录级的元数据 自管理 如果任务失败会自动回滚,并且再次尝试 万物皆日志 内部存储是以日志的形式保存 键-值数据模型 Hudi表被建模为键值对数据集 二、表类型 Copy On Write表(COW)——写时复制 COW表写的时候数据直接写入basefile,(parquet)不写log文件。所以COW表的文.

白色风车 4716

【AI智能体】Claude Code 核心记忆文件 CLAUDE.md实战操作详解

Claude Code 核心记忆文件 CLAUDE.md实战操作详解

congge 5457

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

数据湖之Hudi基础:集成Spark

主要记录下的整合Spark操作,操作内容参考尚硅谷Hudi公开资料以及Hudi官方文档具体参看官方文档:https://hudi.apache.org/docs/0.12.1/quick-start-guide。 Hudi集成spark

maoyuanming0806的博客 2323

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

【Hudi】数据湖

大数据

qq_41466440的博客 1590

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

hudi与spark整合

hudi与spark thrift整合

sinat_42671149的博客 1593

数据湖之Hudi(4):Apache Hudi 快速发展

目录 0. 相关文章链接 1. Hudi 诞生 2.发展历史 3. 各版本新特性 4.新架构:湖仓一体 0. 相关文章链接 大数据基础知识点 文章汇总 1. Hudi 诞生 Apache Hudi由Uber开发并开源,该项目在2016年开始开发,并于2017年开源,2019年1月进入 Apache 孵化器,且2020年6月称为Apache 顶级项目,目前最新版本:0.9.0版本。 Hudi 一开始支持Spark进行数据摄入(批量Batch和流式Streaming),从0.7.0版本开..

yang_shibiao的博客 2896

数据湖之Hudi(3):Apache Hudi 基本介绍

目录 0. 相关文章链接 1. 什么是Hudi 2. Hudi在大数据中的位置 3. Hudi的特性 4. Hudi各版本发布时间 0. 相关文章链接 大数据基础知识点 文章汇总 1. 什么是Hudi Apache Hudi(发音“hoodie”)是下一代流数据湖平台。Apache Hudi将核心仓库和数据库功能直接带到数据湖中。Hudi提供了表,事务,高效upserts /删除,高级索引,流式摄取服务,数据群集/压缩优化以及并发,同时保持数据以开源文件格式保留。 ...

yang_shibiao的博客 3925

数据湖和数据仓库区别介绍

数据湖是用来存储什么样的数据呢? 数据湖是以什么样的存储模式存储数据呢?是关系型数据库的模式吗? 数据湖与Delta Lake的关系是? 数据湖可以替代数据仓库吗? 简单对比下数据湖与数据仓库。 数据湖存储起来非常方便,为了保证敏捷开发,是无需管理的,对吗? Apache Hudi是干什么的?仅仅实现增删改查吗? 基于Hudi的数据湖数据是以什么方式存储的? Hudi有元数据吗?元数据存储在哪儿? Hudi是以什么方式与Spark进行整合的? 1 从数据仓库到数据湖 1 仓库.

hzp666的博客 9266
上一篇: 数据湖(三):Hudi 概念术语
下一篇: 数据湖(五):Hudi 与 Hive 集成
wrr-cat
博客等级 码龄9年 128粉丝 302原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值