Spark RDD/DataFrame map保存数据的两种方式

使用Spark RDD或DataFrame,有时需要在foreachPartition或foreachWith里面保存数据到本地或HDFS。

直接保存数据

当然如果不需要在map里面保存数据,那么针对RDD可以有如下方式

val rdd = // target rdd
rdd.saveAsHadoopFile // add some parameters 

针对DataFrame可以有如下方式保存数据

val df = // target dataframe
// 保存中间数据
df.registerTempTable("temp table name")

// 持久化数据
df.save // 使用save函数,指定模式等参数
df.saveAsParquetFile    // depressed
df.saveAsTable  // depressed

foreach里面保存数据

调用foreachXXX之后,里面的每条记录都是Iterator[YYY]形式的数据,是可迭代数据。

保存到文件

保存到文件相对简单,可以直接使用上面的save保存,例如

def save2HDFS(sc: SparkContext, input: Iterator[Row]): Unit = {
    val result = input.map(item => item.getString(0) + "," + item.getInt(1)).toSeq
    val tmpRDD = sc.parallelize(result)
    tmpRDD.saveAsObjectFile("//path") // 1
    tmpRDD.saveAsTextFile("//path") // 2
    tmpRDD.saveAsTextFile("",CompressClass) // 3 内容编码类,继承自org.apache.hadoop.io.compress.CompressionCodec
  }

保存到数据库

在foreachXXX里面,可以将数据保存到数据库,这里使用的方式为JDBC的方式。

 def save2DB(input: Iterator[Row]): Unit = {

    var temp: Row = null
    while (input.hasNext) {
      temp = input.next // 将迭代数据保存为入库数据
    }

    var dbconn: Connection = null
    var stmt: Statement = null
    try {
      dbconn = DriverManager.getConnection("", "", "")
      stmt = dbconn.createStatement()
      stmt.execute("truncate table TableName")
    } catch {
      case e: Exception => {
        // println(">>>>>>>>>>>>清空表失败")
        // e.printStackTrace()
      }
    } finally {
      { // close connection
        if (stmt != null)
          stmt.close()
        if (dbconn != null)
          dbconn.close()
      }
      { // modify poiner to NULL
        stmt = null
        dbconn = null
      }
    }
  }

DataFrame读入写出操作

DataFrame可以方便的将要各种数据源的数据,读入到内存中,也可以方便的将DF数据写为各种格式的数据。

读入操作

sqlContext.read.jdbc// JDBC数据源
sqlContext.read.json// JSON数据源
sqlContext.read.parquet// Parquet数据源

写出操作

val tarDF =  // target dataframe 
tarDF.write.jdbc// 写入JDBC数据库
tarDF.write.json// 写入JSON数据源
tarDF.write.parquet// 写入Parquet数据源

以上几种数据源,是Spark自身带有驱动程序的。其他文件格式,需要相应的驱动程序,或相应的安装包支持。

### RDDDataFrame 和 DataSet 的关系及区别 #### 定义与特性 RDD(Resilient Distributed Datasets)是 Spark 中最基本的数据抽象,表示一个不可变的分布式数据集。它可以存储任何类型的 Java/Scala 对象,并支持丰富的操作,如 map、filter 等[^1]。 DataFrame 是一种以表格形式组织数据的分布式数据结构,类似于传统的关系型数据库表。它提供了一个更高层次的 API,能够自动优化执行计划,适合处理大规模结构化数据[^2]。 DataSet 则是在 DataFrame 基础上的进一步扩展,结合了强类型的安全性和性能优势。它是 Spark SQL 提供的一种新的接口,既具有 RDD 的灵活性又具备 DataFrame 的优化能力[^3]。 --- #### 主要区别 | 特性 | RDD | DataFrame | DataSet | |-----------------|------------------------------|-------------------------------|--------------------------------| | **类型安全性** | 非类型安全 | 类型不安全 | 类型安全 | | **API 层次** | 低级 | 高级 | 更高级 | | **优化器支持** | 不支持 Catalyst Optimizer | 支持 Catalyst Optimizer | 支持 Catalyst Optimizer | | **序列化开销** | 较高 | 较低 | 较低 | | **适用场景** | 复杂自定义逻辑 | 结构化数据分析 | 强类型需求下的高性能计算 | --- #### 相互转换 - **RDDDataFrame**: 只需调用 `toDF` 方法即可完成转换。 ```scala val rdd = sc.parallelize(Seq((1, "Alice"), (2, "Bob"))) val df = rdd.toDF("id", "name") ``` - **DataFrameRDD**: 使用 `.rdd` 方法可以将 DataFrame 转回 RDD。 ```scala val rddFromDf = df.rdd ``` - **RDD 转 DataSet**: 如果需要将 RDD 转换为 DataSet,则可以通过引入隐式转换并指定目标类来实现[^3]。 ```scala case class Person(name: String, age: Int) val rdd = sc.parallelize(Seq(("Alice", 25), ("Bob", 30))) val ds = rdd.map { case (name, age) => Person(name, age) }.toDS() ``` --- #### 性能对比 由于 DataFrame 和 DataSet 内部都依赖于 Catalyst 查询优化器,因此它们通常比纯 RDD 更高效。特别是在涉及大量复杂查询时,这种差异会更加明显[^2]。 然而,在某些特定情况下,如果业务逻辑非常复杂或者无法直接映射到标准 SQL 或 DSL 表达式上,那么可能仍然需要用到原始的 RDD 接口来进行细粒度控制[^3]。 --- ### 示例代码 以下是展示三者之间基本操作的一个简单例子: ```scala import org.apache.spark.sql.SparkSession import org.apache.spark.sql.functions._ // 初始化 Spark Session val spark = SparkSession.builder.appName("Example").master("local[*]").getOrCreate() // 创建 RDD 并转化为 DataFrame 和 DataSet val data = Seq( ("John", 28), ("Mary", 34), ("James", 40) ) // RDD -> DataFrame val rdd = spark.sparkContext.parallelize(data) val df = rdd.toDF("name", "age") // DataFrame -> RDD val backToRdd = df.rdd // RDD -> DataSet case class User(name: String, age: Int) val ds = rdd.map { case (n, a) => User(n, a) }.toDS() // 执行聚合操作 ds.groupBy($"age").count().show() ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值