Spark 读写数据、抽象转换 拾遗

package com.test.spark

import org.apache.spark.sql.{Dataset, Row, SaveMode, SparkSession}

/**
  * @author Administrator
  *         2019/7/22-17:09
  *
  */
object TestReadData {
  val spark = SparkSession
    .builder()
    .appName("TestCreateDataset")
    .config("spark.some.config.option", "some-value")
    .master("local")
    .enableHiveSupport()
    .getOrCreate()

  def main(args: Array[String]): Unit = {
    testRead
  }

  def testRead(): Unit = {
    //  parquet 如果有损坏啥的容易莫名的错误
    val parquet: Dataset[Row] = spark.read.parquet("D:\\DATA-LG\\PUBLIC\\TYGQ\\INF\\PersonInfoCommon.parquet")
    parquet.show()

    // Spark SQL 的通用输入模式
    val commonRead: Dataset[Row] = spark.read.format("json").load("D:\\DATA-LG\\PUBLIC\\TYGQ\\INF\\testJson")
    commonRead.show()
    // Spark SQL 的通用输出模式
    commonRead.write.format("parquet").mode(SaveMode.Append).save("D:\\DATA-LG\\PUBLIC\\TYGQ\\INF\\PersonInfoCommon.parquet")

    // Spark SQL 的专业输入模式
    val professionalRead: Dataset[Row] = spark.read.json("D:\\DATA-LG\\PUBLIC\\TYGQ\\INF\\testJson")
    professionalRead.show()
    // Spark SQL 的专业输出模式
    professionalRead.write.mode(SaveMode.Append).parquet("D:\\DATA-LG\\PUBLIC\\TYGQ\\INF\\PersonInfoProfessional.parquet")

    val readParquet: Dataset[Row] = spark.sql("select * from parquet.`D:\\DATA-LG\\PUBLIC\\TYGQ\\INF\\PersonInfoCommon.parquet`")
    readParquet.show()
  }

}

//输出:
+---+---------------+---------+
|age|             ip|     name|
+---+---------------+---------+
| 24|    192.168.0.8|  lillcol|
|100|  192.168.255.1|    adson|
| 39|  192.143.255.1|     wuli|
| 20|  192.168.255.1|       gu|
| 15|  243.168.255.9|     ason|
|  1|  108.168.255.1|   tianba|
| 25|222.168.255.110|clearlove|
| 30|222.168.255.110|clearlove|
+---+---------------+---------+

+---+---------------+---------+
|age|             ip|     name|
+---+---------------+---------+
| 24|    192.168.0.8|  lillcol|
|100|  192.168.255.1|    adson|
| 39|  192.143.255.1|     wuli|
| 20|  192.168.255.1|       gu|
| 15|  243.168.255.9|     ason|
|  1|  108.168.255.1|   tianba|
| 25|222.168.255.110|clearlove|
| 30|222.168.255.110|clearlove|
+---+---------------+---------+

+---+---------------+---------+
|age|             ip|     name|
+---+---------------+---------+
| 24|    192.168.0.8|  lillcol|
|100|  192.168.255.1|    adson|
| 39|  192.143.255.1|     wuli|
| 20|  192.168.255.1|       gu|
| 15|  243.168.255.9|     ason|
|  1|  108.168.255.1|   tianba|
| 25|222.168.255.110|clearlove|
| 30|222.168.255.110|clearlove|
+---+---------------+---------+

+---+---------------+---------+
|age|             ip|     name|
+---+---------------+---------+
| 24|    192.168.0.8|  lillcol|
|100|  192.168.255.1|    adson|
| 39|  192.143.255.1|     wuli|
| 20|  192.168.255.1|       gu|
| 15|  243.168.255.9|     ason|
|  1|  108.168.255.1|   tianba|
| 25|222.168.255.110|clearlove|
| 30|222.168.255.110|clearlove|
| 24|    192.168.0.8|  lillcol|
|100|  192.168.255.1|    adson|
| 39|  192.143.255.1|     wuli|
| 20|  192.168.255.1|       gu|
| 15|  243.168.255.9|     ason|
|  1|  108.168.255.1|   tianba|
| 25|222.168.255.110|clearlove|
| 30|222.168.255.110|clearlove|
+---+---------------+---------+


保存

文件保存选项

模式 注释
Append DataFrame的内容将被追加到现有数据中。
Overwrite 现有数据将被数据Daframe的内容覆盖。
ErrorIfExists 如果数据已经存在,报错。
Ignore 如果数据已经存在,不执行任何操作

注:这些保存模式不使用任何锁定,不是原子操作。
如果使用 Overwrite 同时该路径(path)又是数据源路径,要先对数据进行持久化操作,
否则会在读取path之前将该数据删除掉,导致后续lazy 读取数据的时候报文件不存在的错误。


类型之间的转换

之前关于Spark 三中抽象之间的转换老是有些纠结
现在对它们之间的转换做个总结

在 SparkSQL 中 Spark 为我们提供了两个新的抽象,分别是 DataFrame 和 DataSet。
他们和 RDD 有什啥关系呢?
首先从版本的产生上来看:RDD(Spark1.0) —> DataFrame(Spark1.3) —> DataSet(Spark1.6)

如果同样的数据都给到这三个数据结构,他们分别计算之后,都会给出相同的结果。
不同是的他们的执行效率和执行方式。
在后期的 Spark 版本中,DataSet 会逐步取代 RDD 和 DataFrame 成为唯一的 API 接口。
所以后续开发 我更多的面向DataSet进行开发了。

RDD
  1. RDD 弹性分布式数据集,Spark 计算的基石,为用户屏蔽了底层对数据的复杂抽象和处理,为用户提供了一组方便的数据转换与求值方法。
  2. RDD 是一个懒执行的不可变的可以支持 Lambda 表达式的并行数据集合。
  3. RDD 的最大好处就是简单,API 的人性化程度很高。
  4. RDD 的劣势是性能限制,它是一个 JVM 驻内存对象,这也就决定了存在 GC 的限制和数据增加时 Java 序列化成本的升高。
DataFrame
  1. 与 RDD 类似,DataFrame 也是一个分布式数据容器。
  2. 然而 DataFrame 更像传统数据库的二维表格,除了数据以外,还记录数据的结构信息,即 schema。
  3. 与 Hive 类似,DataFrame 也支持嵌套数据类型(struct、array 和 map)。
  4. 从 API 易用性的角度上看,DataFrame API 提供的是一套高层的关系操作,比函数式的 RDD API 要更加友好,门槛更低。
  5. 由于与 R 和 Pandas 的 DataFrame 类似,Spark DataFrame 很好地继承了传统单机数据分析的开发体验。

Q: DataFrame性能上比 RDD 要高的原因:

A:主要有两个原因

  1. 定制化内存管理
    数据以二进制的方式存在于非堆内存,节省了大量空间之外,还摆脱了 GC 的限制。
    DataFrame定制化内存管理.png
  2. 优化的执行计划
    查询计划通过 Spark catalyst optimiser 进行优化。
DataSet
  1. 是 DataFrame API 的一个扩展,是 Spark 最新的数据抽象。
  2. 用户友好的 API 风格,既具有类型安全检查也具有 DataFrame 的查询优化特性。
  3. DataSet 支持编解码器,当需要访问非堆上的数据时可以避免反序列化整个对象,提高了效率。
  4. 样例类被用来在 DataSet 中定义数据的结构信息,样例类中每个属性的名称直接映射到 DataSet 中的字段名称。
  5. DataFrame 是 DataSet 的特列,type DataFrame = Dataset[Row] ,所以可以通过 as 方法将 DataFrame 转换为 DataSet。Row 是一个类型,跟 Car、Person 这些的类型一样,所有的表结构信息都用 Row 来表示。
  6. DataSet 是强类型的。比如可以有 Dataset[Car],Dataset[Person],
    DataFrame 只是知道字段,但是不知道字段的类型,所以在执行这些操作的时候是没办法在编译的时候检查是否类型失败的,比如你可以对一个 String 进行减法操作,在执行的时候才报错,
    而 DataSet 不仅仅知道字段,而且知道字段类型,所以有更严格的错误检查。
    就跟 JSON 对象和类对象之间的类比。

三者之间的转换

case class Person(name: String, age: Long) extends Serializable //case class的定义要在引用case class函数的外面。

import spark.implicits._

//类型之间的转换:注意输出类型
    def rddSetFrame() = {
    // 在使用一些特殊的操作时,一定要加上 import spark.implicits._ 不然 toDF、toDS 无法使用。
    val rdd: RDD[String] = spark.sparkContext.textFile("D:\\DATA-LG\\PUBLIC\\TYGQ\\INF\\testFile")
    val ds: Dataset[Row] = spark.read.json("D:\\DATA-LG\\PUBLIC\\TYGQ\\INF\\testJson")
    val df: DataFrame = rdd.map(_.split(",")).map(strArr => (strArr(0).trim(), strArr(1).trim().toInt)).toDF("nama", "age")

    //    rdd->df
    //一般用元组把一行的数据写在一起,然后在 toDF 中指定字段名。
    val rddTDf: DataFrame = rdd.map(_.split(",")).map(strArr => (strArr(0).trim(), strArr(1).trim().toInt)).toDF("nama", "age")
    //   df -> rdd
    val dfTRdd: RDD[Row] = df.rdd;

    //   rdd -> ds
    //定义每一行的类型 case class 时,已经给出了字段名和类型,后面只要往 case class 里面添加值即可。
    val rddTDs: Dataset[Person] = rdd.map(_.split(",")).map(strArr => Person(strArr(0).trim(), strArr(1).trim().toInt)).toDS()
    //   ds -> rdd
    val dsTRdd: RDD[Person] = rddTDs.rdd

    //    df->ds
    //这种方法就是在给出每一列的类型后,使用 as 方法,转成 DataSet,这在数据类型是 DataFrame 又需要针对各个字段处理时极为方便。
    val dfTDs: Dataset[Person] = df.as[Person]
    //    ds->df
    // 只是把 case class 封装成 Row。
    val dsTDf: DataFrame = ds.toDF

  }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容