百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

四十一、SparkSQL读取parquet数据源(必须要弄懂)

haoteby 2025-05-08 18:31 10 浏览

1.Parquet文件介绍

Apache Parquet是Hadoop生态圈中一种新型的列式存储格式,它可以兼容Hadoop生态圈中大多数据的计算框架,如Hadoop, Spark,它也被多种查询引擎所支持,例如Hive, Impala等,而且它是跨语言和平台的。

Parquet的产生是由Twitter和Cloudera公司由于Apache Impala的缘故使用开发完成并开源给Apache基金会组织进行孵化,现已成为APache的顶级项目。

另一方面,随着嵌套格式数据的需求日益增加,目前Hadoop生态圈中主流的OLAP都支持丰富的数据类型,例如Hive, SparkSQL, Impala等都支持诸如array, map, struct这样的复合数据类型,这也使得像Parquet这种原生支持嵌套数据的存储格式变得至关重要,由于它是列式存储,所以在性能方面会很高。

列式存储,就是按照列进行存储数据,把某一旬的数据连续地存储,每一行中的不同的列离散分布。相比较于行存储,列存储具有以下优势:

  • 可以跳过不符合条件的数据,只读取需要的数据,降低磁盘IO
  • 使用压缩可以降低磁盘的存储空间,并且由于同一列的数据类型是一样的,可以使用更高效的压缩编码进一步节约存储空间;
  • 只读取需要的列,能够获得更好的扫描性能;

Parquet是SparkSQL默认的存储格式,它支持灵活的读写Parquet文件,并对Parquet文件的schema可以自动解析。


import org.apache.spark.sql.{DataFrame, SparkSession}

object SparkSqlParquetSource {
  def main(args: Array[String]): Unit = {
    val spark: SparkSession = SparkSession.builder()
      .appName(SparkSqlParquetSource.getClass.getSimpleName)
      .master("local")
      .getOrCreate()

    spark.sparkContext.setLogLevel("WARN")

    //读取json文件生成DataFrame
    val sanguoDF: DataFrame = spark.read.format("json").load("./dataset/sanguo.json")

    //把结果写入parquet
    sanguoDF.write.parquet("./dataset/parquet/sanguo.parquet")

    /**
     * 读取刚刚写入的parquet文件
     *
     */
    val sgDF: DataFrame = spark.read.parquet("./dataset/parquet/sanguo.parquet")

    //打印schema
    sgDF.printSchema()
    sgDF.show()

    //释放资源
    spark.stop()
  }
}

上述代码中,通过读取json文件写入parquet文件:


2.分区发现(Partition Discovery)

表分区是一种常见的优化方法,比如Hive中就提供了分区表的特性。在一个分区表中,不同分区的数据通常是存储在HDFS上不同的目录中,分区列的值通常就包含在了分区目录的目录名中。SparkSQL中的parquet数据源,支持自动根据目录名推断出分区信息。例如,如果将人口数据存储在分区表中,并且使用性别和国家作为分区列。那么目录结构可能如下所示:

path
└── to
    └── table
        ├── gender=male
        │   ├── ...
        │   │
        │   ├── country=US
        │   │   └── data.parquet
        │   ├── country=CN
        │   │   └── data.parquet
        │   └── ...
        └── gender=female
            ├── ...
            │
            ├── country=US
            │   └── data.parquet
            ├── country=CN
            │   └── data.parquet
            └── ...

如果将 path/to/table传入SparkSession.read.parquet()或SparkSession.read.load()方法,那么SparkSQL就会自动根据目录的结构,推断出分区信息是gender和country。即使数据文件中只包含了两列值,name和age,但是Spark SQL返回的DataFrame,调用printSchema()方法时,会打印出四个列的值:name,age,country,gender。这就是自动分区推断的功能。

此外,分区列的数据类型,也是自动被推断出来的。目前,Spark SQL仅支持自动推断出数字类型和字符串类型。有时,用户也许不希望Spark SQL自动推断分区列的数据类型。此时只要设置一个配置即可,
spark.sql.sources.partitionColumnTypeInference.enabled
,默认为true,即自动推断分区列的类型,设置为false,即不会自动推断类型。禁止自动推断分区列的类型时,所有分区列的类型,就统一默认都是String。

3.元数据合并(Schema Merging)

如同ProtocolBuffer,Avro,Thrift一样,Parquet也是支持元数据合并的。用户可以在一开始就定义一个简单的元数据,然后随着业务需要,逐渐往元数据中添加更多的列。在这种情况下,用户可能会创建多个Parquet文件,有着多个不同的但是却互相兼容的元数据。Parquet数据源支持自动推断出这种情况,并且进行多个Parquet文件的元数据的合并。

因为元数据合并是一种相对耗时的操作,而且在大多数情况下不是一种必要的特性,从Spark 1.5.0版本开始,默认是关闭Parquet文件的自动合并元数据的特性的。可以通过以下两种方式开启Parquet数据源的自动合并元数据的特性:

  1. 读取Parquet文件时,将数据源的选项,mergeSchema,设置为true
  2. 将spark.sql.parquet.mergeSchema参数设置为true

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

object SparkSqlSchemaMergeTest {
  def main(args: Array[String]): Unit = {
    val spark: SparkSession= SparkSession.builder()
      .master("local")
      .appName(SparkSqlSchemaMergeTest.getClass.getSimpleName)
      .getOrCreate()

    spark.sparkContext.setLogLevel("WARN")

    //导入隐式转换
    import spark.implicits._

    //创建第一个DataFrame
    val personSeq: Seq[(String, Int)] = Array(("风清扬", 55), ("任我行", 60)).toSeq
    val personDF: DataFrame = spark.createDataset(personSeq).toDF("name", "age")
//    personDF.printSchema()
//    personDF.show()
    //保存第一个DF到parquet文件
    personDF.write.mode(SaveMode.Append).parquet("./dataset/parquet/person.parquet")

    //创建第二个DataFrame
    val personWithGenderSeq: Seq[(String, String)] = Array(("关羽", "男"), ("张飞", "男")).toSeq
    val personWithGenderDF: DataFrame = spark.createDataset(personWithGenderSeq).toDF("name", "gender")
    //保存第二个DF到parquet文件
    personWithGenderDF.write.mode(SaveMode.Append).parquet("./dataset/parquet/person.parquet")

    /**
     * 首先,第一个DataFrame和第二个DataFrame的元数据是肯定不一样的
     * 一个是包含了name和age, 而别一个是包含了name和gender
     * 所以,期望将来读取这个parquet文件时只有三列name, age, gender, 实现自动合并元数据的功能
     *
     */

    //用mergeSchema的方式,读取person.parquet文件中的数据,并将元数据合并
    val mergedPerosnDF: DataFrame = spark.read.format("parquet").option("mergeSchema", "true").load("./dataset/parquet/person.parquet")
    //打印合并的schema的信息
    mergedPerosnDF.printSchema()
    mergedPerosnDF.show()

    spark.stop()

  }
}

相关推荐

网站seo该怎么优化

一、网站定位在建设一个网站之前,我们首先要做的就是一个网站清晰的定位,会带来转化率相对较高的客户群体,我们建站的目的就是为了营销,只有集中来做某一件事,才会更好的展现我们的网站。在做SEO优化的同时...

3个小技巧教你如何做好SEO优化

  想半路出家做SEO?可是,怎么才做的好呢?关于SEO专业技术弄懂搜索引擎原理,咱们做搜索引擎排名的首先就是要了解搜索引擎的工作原理,对SEO优化有更深入了解之后再来做SEO,你就能从搜索引擎的视点...

SEO指令分享:filetype指令

filetype用于搜索特定的文件格式。百度和谷歌都支持filetype指令。比如搜索filetype:pdf今日头条返回的就是包含今日头条这个关键词的所有pdf文件,如下图:百度只支持:pdf...

网站seo优化技巧大全

SEO在搜索引擎中对检索结果进行排序,看谁最初是在用户的第一眼中看到的。实际上,这些排名都是通过引擎的内部算法来实现的。例如,百度算法很有名。那么,对百度SEO的优化有哪些小技巧?下面小编就会说下针对...

小技巧#10 某些高级的搜索技巧

由于某些原因,我的实验场所仅限百度。1.关键词+空格严格说来这个不能算高级,但关键词之间打空格的办法确实好用。我习惯用右手大拇指外侧敲击空格键,这个习惯在打英文报告时尤其频繁。2.site:(请不要忽...

MYSQL数据库权限与安全

权限与安全数据库的权限和数据库的安全是息息相关的,不当的权限设置可能会导致各种各样的安全隐患,操作系统的某些设置也会对MySQL的安全造成影响。1、权限系统的工作原理...

WPF样式

UniformGrid容器<UniformGridColumns="3"Rows="3"><Button/>...

mysql自动备份,并zabbix检测备份文件是否正常,备份文件大小

推荐...

MySQL学到什么程度?才有可以在简历上写精通

前言如今互联网行业用的最多就是MySQL,然而对于高级Web面试者,尤其对于寻找30k下工作的求职者,很多MySQL相关知识点基本都会涉及,如果面试中,你的相关知识答的模糊和不切要点,基...

jquery的事件名称和命名空间的方法

我们先看一些代码:当然,我们也可以用bind进行事件绑定。我们看到上面的代码,我们可以在事件后面,以点号,加我们的名字,就是事件命名空间。所谓事件命名空间,就是事件类型后面以点语法附加一个别名,以便引...

c#,委托与事件,发布订阅模型,观察者模式

什么是事件?事件(Event)基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。通过委托使用事件事件在类中声明且生成,且通过...

前端分享-原生Popover已经支持

传统网页弹窗开发需要自己处理z-index层级冲突、编写点击外部关闭的逻辑、管理多个弹窗的堆叠顺序。核心优势对比:...

Axure 8.0 综合帖——新增细节内容

一、钢笔工具与PS或者AI中的钢笔工具一样的用法。同样有手柄和锚点,如果终点和起点没有接合在一起,只要双击鼠标左键即可完成绘画。画出来的是矢量图,可以理解为新的元件。不建议通过这个工具来画ICON图等...

PostgreSQL技术内幕28:触发器实现原理

0.简介在PostgreSQL(简称PG)数据库中,触发器(Trigger)能够在特定的数据库数据变化事件(如插入、更新、删除等)或数据库事件(DDL)发生时自动执行预定义的操作。触发器的实现原理涉及...

UWP开发入门(十七)--判断设备类型及响应VirtualKey

蜀黍我做的工作跟IM软件有关,UWP同时会跑在电脑和手机上。电脑和手机的使用习惯不尽一致,通常我倾向于根据窗口尺寸来进行布局的变化,但是特定的操作习惯是依赖于设备类型,而不是屏幕尺寸的,比如聊天窗口的...