Spark中文文档翻译3.1.1-Spark SQL Guide--dataSource

如果觉得内容不错,别忘记一键三连哦!!!
如果觉得内容不错,别忘记一键三连哦!!!
如果觉得内容不错,别忘记一键三连哦!!!

Data Sources

Spark SQL支持通过DataFrame接口在各种数据源上运行。数据帧可以使用关系转换进行操作,也可以用来创建临时视图。将数据帧注册为临时视图允许对其数据运行SQL查询。本节介绍使用Spark数据源加载和保存数据的一般方法,然后介绍内置数据源可用的特定选项。

Generic Load/Save Functions

在最简单的形式中,默认的数据源(parquet,除非由spark.sql.sources.default配置)将用于所有操作。

val usersDF = spark.read.load("examples/src/main/resources/users.parquet")
usersDF.select("name", "favorite_color").write.save("namesAndFavColors.parquet")

Manually Specifying Options(手动指定选项)

您还可以手动指定将要使用的数据源,以及您希望传递给数据源的任何额外选项。数据源是由它们的完全限定名指定的(例如,org.apache.spark.sql.parquet),但是对于内置的数据源,你也可以使用它们的短名称(json, parquet, jdbc, orc, libsvm, csv, text)。从任何数据源类型加载的DataFrames 都可以使用此语法转换为其他类型。

请参考API文档了解内置源的可用选项,例如:org.apache.spark.sql. dataframerreader和org.apache.spark.sql.DataFrameWriter。文档中的选项也应该适用于非scala的Spark api(例如PySpark)。对于其他格式,请参考特定格式的API文档。

要加载JSON文件,您可以使用

val peopleDF = spark.read.format("json").load("examples/src/main/resources/people.json")
peopleDF.select("name", "age").write.format("parquet").save("namesAndAges.parquet")

要加载CSV文件,您可以使用

val peopleDFCsv = spark.read.format("csv")
  .option("sep", ";")
  .option("inferSchema", "true")
  .option("header", "true")
  .load("examples/src/main/resources/people.csv")

在写操作期间还会使用额外的选项。 例如,您可以控制ORC数据源的Bloom过滤器和字典编码。 以下ORC示例将创建bloom过滤器,并且仅对favourite_color使用字典编码。 对于Parquet,也存在parquet.enable.dictionary。 要查找有关其他ORC / Parquet选项的更多详细信息,请访问Apache ORC / Parquet官方网站。

usersDF.write.format("orc")
  .option("orc.bloom.filter.columns", "favorite_color")
  .option("orc.dictionary.key.threshold", "1.0")
  .option("orc.column.encoding.direct", "name")
  .save("users_with_options.orc")

Run SQL on files directly(直接文件上运行SQL)

除了使用read API将文件加载到数据框架中并进行查询外,您还可以直接使用SQL查询该文件。

val sqlDF: DataFrame = spark.sql("SELECT * FROM parquet.`examples/src/main/resources/users.parquet`")

Save Modes

保存操作可以选择采用SaveMode,它指定如何处理存在的数据。重要的是要认识到,这些保存模式不使用任何锁定,也不是原子的。此外,当执行覆盖时,数据将在写入新数据之前被删除。

Scala/JavaAny LanguageMeaning
SaveMode.ErrorIfExists (default)"error" or "errorifexists" (default)当将数据帧保存到数据源时,如果数据已经存在,则预期会抛出异常。
SaveMode.Append“append”将DataFrame保存到数据源时,如果已经存在数据/表,则应该将DataFrame的内容附加到现有数据中。
SaveMode.Overwrite“overwrite”覆盖模式意味着将DataFrame保存到数据源时,如果已经存在数据/表,则预期现有数据将被DataFrame的内容覆盖。
SaveMode.Ignore“ignore”忽略模式是指在将数据帧保存到数据源时,如果数据已经存在,则save操作不会保存数据帧的内容,也不会改变现有的数据。这类似于SQL中不存在的CREATE表。

Saving to Persistent Tables

DataFrames也可以通过saveAsTable命令保存为持久化表到Hive metastore中。注意使用该特性时,不需要使用已有的Hive部署。Spark将为您创建一个默认的本地Hive metastore(使用Derby)。与createOrReplaceTempView命令不同,saveAsTable将实现DataFrame的内容,并创建一个指向Hive metastore数据的指针。即使在Spark程序重新启动后,持久表仍然存在,只要您维护到同一个元存储的连接。可以创建一个持久化表的数据帧

对于基于文件的数据源,例如text, parquet, json, etc. 您可以通过path选项指定自定义表路径df.write.option("path", "/some/path").saveAsTable("t").当表被删除时,自定义表路径不会被删除,表数据仍然存在。如果没有指定自定义表路径,Spark会将数据写入仓库目录下的默认表路径。当表被删除时,默认的表路径也将被删除。

从Spark 2.1开始,持久化数据源表的每个分区元数据都存储在Hive metastore中。这带来了几个好处

  • 由于metastore只能为查询返回必要的分区,所以不再需要在第一个查询中发现表的所有分区。
  • 如:ALTER TABLE PARTITION…SET LOCATION现在可以用于使用数据源API创建的表。

Bucketing, Sorting and Partitioning

对于基于文件的数据源,还可以对输出进行存储、排序或分区。存储和排序仅适用于持久表

peopleDF.write.bucketBy(42, "name").sortBy("age").saveAsTable("people_bucketed")

而当使用数据集api时,分区可以与save和saveAsTable一起使用。

usersDF.write.partitionBy("favorite_color").format("parquet").save("namesPartByColor.parquet")

可以对单个表同时使用分区和存储

usersDF
  .write
  .partitionBy("favorite_color")
  .bucketBy(42, "name")
  .saveAsTable("users_partitioned_bucketed")

partitionBy创建分区发现部分中描述的目录结构。因此,它对具有高基数的列的适用性有限。相比之下,bucketBy将数据分布在固定数量的桶中,并且可以在唯一值的数量没有限制的情况下使用。

Generic File Source Options

请注意,下面示例中使用的目录的层次结构是

dir1/
 ├── dir2/
 │    └── file2.parquet (schema: <file: string>, content: "file2.parquet")
 └── file1.parquet (schema: <file, string>, content: "file1.parquet")
 └── file3.json (schema: <file, string>, content: "{'file':'corrupt.json'}")

Ignore Corrupt Files(忽略损坏的文件)

Spark允许你使用Spark .sql.files. ignorecorruptfiles在从文件中读取数据时忽略损坏的文件。当设置为true时,Spark作业将在遇到损坏的文件时继续运行,并且仍然会返回已读取的内容。

要在读取数据文件时忽略损坏的文件,可以使用

// enable ignore corrupt files
spark.sql("set spark.sql.files.ignoreCorruptFiles=true")
// dir1/file3.json is corrupt from parquet's view
val testCorruptDF = spark.read.parquet(
  "examples/src/main/resources/dir1/",
  "examples/src/main/resources/dir1/dir2/")
testCorruptDF.show()
// +-------------+
// |         file|
// +-------------+
// |file1.parquet|
// |file2.parquet|
// +-------------+

Ignore Missing Files

Spark允许你使用Spark .sql.files. ignoremissingfiles在从文件中读取数据时忽略丢失的文件。这里,missing file实际上是指在构建数据帧之后在目录下删除的文件。当设置为true时,Spark作业将在遇到丢失的文件时继续运行,并且仍然会返回已读取的内容。

Path Global Filter

pathGlobFilter只用于包含文件名与模式匹配的文件。语法遵循org.apache.hadoop.fs.GlobFilter。它不会改变分区发现的行为。要加载路径与给定的glob模式匹配的文件,同时保持分区发现的行为,可以使用

val testGlobFilterDF = spark.read.format("parquet")
  .option("pathGlobFilter", "*.parquet") // json file should be filtered out
  .load("examples/src/main/resources/dir1")
testGlobFilterDF.show()
// +-------------+
// |         file|
// +-------------+
// |file1.parquet|
// +-------------+

Recursive File Lookup

recursiveFileLookup用于递归加载文件,它禁用分区推断。默认值为false。如果当recursiveFileLookup为true时data source显式地指定了partitionSpec,则会抛出异常。

val recursiveLoadedDF = spark.read.format("parquet")
  .option("recursiveFileLookup", "true")
  .load("examples/src/main/resources/dir1")
recursiveLoadedDF.show()
// +-------------+
// |         file|
// +-------------+
// |file1.parquet|
// |file2.parquet|
// +-------------+

Modification Time Path Filters

modifiedBefore和modifiedAfter是可以一起应用或单独应用的选项,以便在Spark批处理查询期间实现更大的文件加载粒度。(注意,结构化流媒体文件源不支持这些选项。)

当没有提供时区选项时,时间戳将根据Spark会话时区解释(Spark .sql.session. timezone)。

val beforeFilterDF = spark.read.format("parquet")
  // Files modified before 07/01/2020 at 05:30 are allowed
  .option("modifiedBefore", "2020-07-01T05:30:00")
  .load("examples/src/main/resources/dir1");
beforeFilterDF.show();
// +-------------+
// |         file|
// +-------------+
// |file1.parquet|
// +-------------+
val afterFilterDF = spark.read.format("parquet")
   // Files modified after 06/01/2020 at 05:30 are allowed
  .option("modifiedAfter", "2020-06-01T05:30:00")
  .load("examples/src/main/resources/dir1");
afterFilterDF.show();
// +-------------+
// |         file|
// +-------------+
// +-------------+

Parquet Files

Parquet是一种列式格式,许多其他数据处理系统都支持这种格式。Spark SQL提供了对自动保存原始数据模式的Parquet文件的读写支持。在读取Parquet文件时,出于兼容性的原因,所有列都会自动转换为可为空的。

Loading Data Programmatically

// Encoders for most common types are automatically provided by importing spark.implicits._
import spark.implicits._

val peopleDF = spark.read.json("examples/src/main/resources/people.json")

// DataFrames can be saved as Parquet files, maintaining the schema information
peopleDF.write.parquet("people.parquet")

// Read in the parquet file created above
// Parquet files are self-describing so the schema is preserved
// The result of loading a Parquet file is also a DataFrame
val parquetFileDF = spark.read.parquet("people.parquet")

// Parquet files can also be used to create a temporary view and then used in SQL statements
parquetFileDF.createOrReplaceTempView("parquetFile")
val namesDF = spark.sql("SELECT name FROM parquetFile WHERE age BETWEEN 13 AND 19")
namesDF.map(attributes => "Name: " + attributes(0)).show()
// +------------+
// |       value|
// +------------+
// |Name: Justin|
// +------------+

Partition Discovery

表分区是Hive等系统中常用的一种优化方法。在分区表中,数据通常存储在不同的目录中,分区列值编码在每个分区目录的路径中。所有内置文件源(including Text/CSV/JSON/ORC/Parquet) 能够自动发现和推断分区信息,例如,我们可以使用下面的目录结构将所有以前使用的人口数据存储到一个分区表中,并添加两个额外的列,gender和country作为分区列

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 传递给 sparkession.read.parquet 或 sparkession.read.load,Spark SQL 将自动从路径中提取分区信息。现在返回的 DataFrame 的模式变成了:

root
|-- name: string (nullable = true)
|-- age: long (nullable = true)
|-- gender: string (nullable = true)
|-- country: string (nullable = true)

请注意,分区列的数据类型是自动推断的。目前,支持数值数据类型、日期、时间戳和字符串类型。有时用户可能不希望自动推断分区列的数据类型。对于这些用例,可以通过 spark.sql.sourcees.partitioncolumntypeinference.enabled 配置自动类型推断,默认为 true。禁用类型推断时,将对分区列使用字符串类型。

从 Spark 1.6.0开始,分区发现在默认情况下只能在给定路径下找到分区。对于上面的示例,如果用户将 path/to/table/gender = male 传递给 sparkession.read.parquet 或 sparkession.read.load,则 gender 不会被视为分区列。如果用户需要指定分区发现应该从哪个基本路径开始,他们可以在数据源选项中设置 basePath。例如,当 path/to/table/gender = male 是数据的路径,用户将 basePath 设置为 path/to/table/时,gender 将是一个分区列。

Schema Merging

像 Protocol Buffer、 Avro 和 Thrift 一样,Parquet 也支持模式进化。用户可以从一个简单的模式开始,然后根据需要逐步向模式添加更多的列。通过这种方式,用户可能最终得到具有不同但相互兼容的模式的多个 Parquet 文件。Parquet 数据源现在能够自动检测这种情况并合并所有这些文件的模式。

由于模式合并是一个相对昂贵的操作,并且在大多数情况下不是必需的,因此我们从1.5.0开始默认关闭它。你可以通过以下方式启用它:

  1. setting data source option mergeSchema to true when reading Parquet files (as shown in the examples below), or
  2. setting the global SQL option spark.sql.parquet.mergeSchema to true.
// This is used to implicitly convert an RDD to a DataFrame.
import spark.implicits._

// Create a simple DataFrame, store into a partition directory
val squaresDF = spark.sparkContext.makeRDD(1 to 5).map(i => (i, i * i)).toDF("value", "square")
squaresDF.write.parquet("data/test_table/key=1")

// Create another DataFrame in a new partition directory,
// adding a new column and dropping an existing column
val cubesDF = spark.sparkContext.makeRDD(6 to 10).map(i => (i, i * i * i)).toDF("value", "cube")
cubesDF.write.parquet("data/test_table/key=2")

// Read the partitioned table
val mergedDF = spark.read.option("mergeSchema", "true").parquet("data/test_table")
mergedDF.printSchema()

// The final schema consists of all 3 columns in the Parquet files together
// with the partitioning column appeared in the partition directory paths
// root
//  |-- value: int (nullable = true)
//  |-- square: int (nullable = true)
//  |-- cube: int (nullable = true)
//  |-- key: int (nullable = true)

Hive metastore Parquet table conversion

当从 Hive metabstore Parquet 表读取数据并写入到未分区的 Hive metabstore Parquet 表时,Spark SQL 将尝试使用自己的 Parquet 支持,而不是 Hive SerDe,以获得更好的性能。此行为由 spark.sql.hive.convertmetastoreppatquet 配置控制,默认情况下是打开的。

Hive/Parquet Schema Reconciliation Hive/Parquet

从表模式处理的角度来看,Hive 和 Parquet 有两个关键区别。

1、Hive is case insensitive, while Parquet is not

2、Hive considers all columns nullable, while nullability in Parquet is significant Hive

由于这个原因,在将 Hive 元存储 Parquet 表转换为 Spark SQL Parquet 表时,我们必须协调 Hive 元存储模式和 Parquet 模式。调节规则如下:

1、两个架构中具有相同名称的字段必须具有相同的数据类型,而不管是否为空。调和的字段应该具有Parquet 一边的数据类型,这样可以考虑空性。

2、协调的Parquet 恰好包含在 Hive 元存储Parquet 中定义的字段。

只出现在 Parquet 模式中的任何字段都将被删除到 conciled 模式中

只出现在 Hive 元存储架构中的任何字段都将作为可空字段添加到调和架构中

Metadata Refreshing

Spark SQL 缓存 Parquet 元数据以获得更好的性能。当启用 Hive metabstore Parquet 表转换时,这些转换后的表的元数据也会被缓存。如果这些表是由 Hive 或其他外部工具更新的,则需要手动刷新它们以确保元数据的一致性。

// spark is an existing SparkSession
spark.catalog.refreshTable("my_table")

Configuration

可以使用 SparkSession 上的 setConf 方法或者使用 SQL 运行 SET key = value 命令来配置 Parquet。

Property NameDefaultMeaningSince Version
spark.sql.parquet.binaryAsStringfalse其他一些产生 Parquet 的系统,特别是 Impala、 Hive 和旧版本的 Spark SQL,在写出 Parquet 模式时不区分二进制数据和字符串。此标志告诉 Spark SQL 将二进制数据解释为字符串,以提供与这些系统的兼容性1.1.1
spark.sql.parquet.int96AsTimestamptrue一些Parquet-producing系统,特别是 Impala 和 Hive,将时间戳存储到 INT96中。这个标志告诉 Spark SQL 将 INT96数据解释为时间戳,以提供与这些系统的兼容性1.3.0
spark.sql.parquet.compression.codecsnappy设置编写Parquet文件时使用的压缩编解码器。 如果在表特定的选项/属性中指定了compression或parquet.compression,则优先级将为Compression,parquet.compression,spark.sql.parquet.compression.codec。 可接受的值包括:none,uncompressed,snappy,gzip,lzo,brotli,lz4,zstd。 请注意,zstd要求在Hadoop 2.9.0之前安装ZStandardCodec,而brotli则需要安装BrotliCodec。1.1.1
spark.sql.parquet.filterPushdowntrue当设置为 true 时,启用push-down过滤器下推优化1.2.0
spark.sql.hive.convertMetastoreParquettrue当设置为 false 时,Spark SQL 将使用 Hive SerDe 作为实木复合地板表,而不是内置的支持1.1.1
spark.sql.parquet.mergeSchemafalse如果为 true,则 Parquet 数据源合并从所有数据文件中收集的模式,否则,如果没有可用的摘要文件,则从摘要文件或随机数据文件中选择模式。1.5.0
spark.sql.parquet.writeLegacyFormatfalse如果为真,数据将以 Spark 1.4和更早的方式写入。例如,十进制值将以 Apache Parquet 的固定长度字节数组格式编写,其他系统如 Apache Hive 和 Apache Impala 使用这种格式。如果错误,将使用拼花中更新的格式。例如,小数将以基于 int 的格式编写。如果 Parquet 输出用于不支持这种新格式的系统,设置为 true1.6.0

ORC Files

从Spark 2.3开始,Spark支持矢量化ORC读取器,其ORC文件格式为新的ORC文件格式。 为此,新添加了以下配置。 当spark.sql.orc.impl设置为native并且spark.sql.orc.enableVectorizedReader设置为true时,矢量化阅读器用于本机ORC表(例如,使用子句USING ORC创建的表)。 对于Hive ORC Serde表(例如,使用子句USING HIVE OPTIONS(fileFormat’ORC’)创建的表),当spark.sql.hive.convertMetastoreOrc也设置为true时,将使用矢量化读取器。

Property NameDefaultMeaningSince Version
spark.sql.orc.implnativeORC实现的名称。它可以nativeandhive.native其中一种。native表示原生ORC支持。hive是hive中的ORC库。2.3.0
spark.sql.orc.enableVectorizedReadertrue在本机实现中启用向量化orc解码。如果为false,则在本机实现中使用新的非向量化ORC读取器。对于hive实现,这被忽略。2.3.0

JSON Files

Spark SQL 可以自动推断 JSON 数据集的schema ,并将其作为 Dataset [ Row ]加载。这个转换可以使用 Dataset [ String ]或 JSON 文件上的 SparkSession.read.json()来完成。

注意,作为 JSON 文件提供的文件不是典型的 JSON 文件。每一行必须包含一个独立的、自包含的有效 JSON 对象。有关更多信息,请参见 JSON 行文本格式,也称为新行分隔的 JSON。

对于常规的多行 JSON 文件,将 multiLine 选项设置为 true。

// Primitive types (Int, String, etc) and Product types (case classes) encoders are
// supported by importing this when creating a Dataset.
import spark.implicits._

// A JSON dataset is pointed to by path.
// The path can be either a single text file or a directory storing text files
val path = "examples/src/main/resources/people.json"
val peopleDF = spark.read.json(path)

// The inferred schema can be visualized using the printSchema() method
peopleDF.printSchema()
// root
//  |-- age: long (nullable = true)
//  |-- name: string (nullable = true)

// Creates a temporary view using the DataFrame
peopleDF.createOrReplaceTempView("people")

// SQL statements can be run by using the sql methods provided by spark
val teenagerNamesDF = spark.sql("SELECT name FROM people WHERE age BETWEEN 13 AND 19")
teenagerNamesDF.show()
// +------+
// |  name|
// +------+
// |Justin|
// +------+

// Alternatively, a DataFrame can be created for a JSON dataset represented by
// a Dataset[String] storing one JSON object per string
val otherPeopleDataset = spark.createDataset(
  """{"name":"Yin","address":{"city":"Columbus","state":"Ohio"}}""" :: Nil)
val otherPeople = spark.read.json(otherPeopleDataset)
otherPeople.show()
// +---------------+----+
// |        address|name|
// +---------------+----+
// |[Columbus,Ohio]| Yin|
// +---------------+----+

Hive Tables

Spark SQL 还支持读写存储在 Apache Hive 中的数据。但是,由于 Hive 有大量依赖项,这些依赖项不包含在默认的 Spark 分发中。如果在类路径上可以找到 Hive 依赖项,Spark 将自动加载它们。请注意,这些 Hive 依赖项还必须出现在所有工作节点上,因为它们需要访问 Hive 序列化和反序列化库(SerDes) ,以便访问存储在 Hive 中的数据。

Hive 的配置是通过将您的 Hive-site.xml、 core-site.xml (用于安全配置)和 hdfs-site.xml (用于 HDFS 配置)文件放在 conf/中来完成的。

在使用 Hive 时,必须使用 Hive 支持实例化 SparkSession,包括对持久 Hive 元存储的连接、对 Hive serdes 的支持以及 Hive 用户定义函数。没有现有 Hive 部署的用户仍然可以启用 Hive 支持。当不被 hive-site.xml 配置时,上下文会自动在工作目录文件中创建 metastore _ db,并创建一个由 Spark.sql.warehouse.dir 配置的目录,该目录默认为 Spark 应用程序启动的工作目录文件中的目录 Spark-warehouse。请注意,hive-site.xml 中的 hive.metastore.warehouse.dir 属性自 Spark 2.0.0以来已被弃用。相反,使用 spark.sql.warehouse.dir 指定 warehouse 中数据库的默认位置。您可能需要向启动 Spark 应用程序的用户授予写权限。

import java.io.File

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

case class Record(key: Int, value: String)

// warehouseLocation points to the default location for managed databases and tables
val warehouseLocation = new File("spark-warehouse").getAbsolutePath

val spark = SparkSession
  .builder()
  .appName("Spark Hive Example")
  .config("spark.sql.warehouse.dir", warehouseLocation)
  .enableHiveSupport()
  .getOrCreate()

import spark.implicits._
import spark.sql

sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING) USING hive")
sql("LOAD DATA LOCAL INPATH 'examples/src/main/resources/kv1.txt' INTO TABLE src")

// Queries are expressed in HiveQL
sql("SELECT * FROM src").show()
// +---+-------+
// |key|  value|
// +---+-------+
// |238|val_238|
// | 86| val_86|
// |311|val_311|
// ...

// Aggregation queries are also supported.
sql("SELECT COUNT(*) FROM src").show()
// +--------+
// |count(1)|
// +--------+
// |    500 |
// +--------+

// The results of SQL queries are themselves DataFrames and support all normal functions.
val sqlDF = sql("SELECT key, value FROM src WHERE key < 10 ORDER BY key")

// The items in DataFrames are of type Row, which allows you to access each column by ordinal.
val stringsDS = sqlDF.map {
  case Row(key: Int, value: String) => s"Key: $key, Value: $value"
}
stringsDS.show()
// +--------------------+
// |               value|
// +--------------------+
// |Key: 0, Value: val_0|
// |Key: 0, Value: val_0|
// |Key: 0, Value: val_0|
// ...

// You can also use DataFrames to create temporary views within a SparkSession.
val recordsDF = spark.createDataFrame((1 to 100).map(i => Record(i, s"val_$i")))
recordsDF.createOrReplaceTempView("records")

// Queries can then join DataFrame data with data stored in Hive.
sql("SELECT * FROM records r JOIN src s ON r.key = s.key").show()
// +---+------+---+------+
// |key| value|key| value|
// +---+------+---+------+
// |  2| val_2|  2| val_2|
// |  4| val_4|  4| val_4|
// |  5| val_5|  5| val_5|
// ...

// Create a Hive managed Parquet table, with HQL syntax instead of the Spark SQL native syntax
// `USING hive`
sql("CREATE TABLE hive_records(key int, value string) STORED AS PARQUET")
// Save DataFrame to the Hive managed table
val df = spark.table("src")
df.write.mode(SaveMode.Overwrite).saveAsTable("hive_records")
// After insertion, the Hive managed table has data now
sql("SELECT * FROM hive_records").show()
// +---+-------+
// |key|  value|
// +---+-------+
// |238|val_238|
// | 86| val_86|
// |311|val_311|
// ...

// Prepare a Parquet data directory
val dataDir = "/tmp/parquet_data"
spark.range(10).write.parquet(dataDir)
// Create a Hive external Parquet table
sql(s"CREATE EXTERNAL TABLE hive_bigints(id bigint) STORED AS PARQUET LOCATION '$dataDir'")
// The Hive external table should already have data
sql("SELECT * FROM hive_bigints").show()
// +---+
// | id|
// +---+
// |  0|
// |  1|
// |  2|
// ... Order may vary, as spark processes the partitions in parallel.

// Turn on flag for Hive Dynamic Partitioning
spark.sqlContext.setConf("hive.exec.dynamic.partition", "true")
spark.sqlContext.setConf("hive.exec.dynamic.partition.mode", "nonstrict")
// Create a Hive partitioned table using DataFrame API
df.write.partitionBy("key").format("hive").saveAsTable("hive_part_tbl")
// Partitioned column `key` will be moved to the end of the schema.
sql("SELECT * FROM hive_part_tbl").show()
// +-------+---+
// |  value|key|
// +-------+---+
// |val_238|238|
// | val_86| 86|
// |val_311|311|
// ...

spark.stop()

Specifying storage format for Hive tables

创建Hive表时,需要定义该表应如何从文件系统读取/写入数据。i.e. the “input format” and “output format”。您还需要定义该表如何将数据反序列化为 rows,或者将rows序列化为数据,即“ serde”。以下选项可用于指定存储格式(“ serde”、“ input format”、“ output format”) ,例如 CREATE TABLE src (id int) USING hive OPTIONS (fileFormat‘ parquet’)。默认情况下,我们将表文件读为纯文本。注意,在创建表的时候还不支持Hive存储处理程序,您可以在 Hive 端使用存储处理程序创建表,并使用 Spark SQL 读取它。

Property NameMeaning
fileFormatfileFormat 是一种存储格式规范的包,包括“ serde”、“ input format”和“ output format”。目前我们支持6种 fileFormats: ‘ sequencefile’、‘ rcfile’、‘ orc’、‘ parquet’、‘ textfile’和‘ avro’
inputFormat, outputFormat这两个选项将相应的InputFormat和OutputFormat类的名称指定为字符串字面量,例如org.apache.hadoop.hive.ql.io.orc.OrcInputFormat。这两个选项必须成对出现,如果您已经指定了fileFormat选项,则不能指定它们。
serde此选项指定serde类的名称。当指定了fileFormat选项时,如果给定的fileFormat已经包含serde的信息,则不要指定此选项。目前"sequencefile", "textfile"和"rcfile"不包括serde信息,你可以使用这个选项与这3个文件格式。
fieldDelim, escapeDelim, collectionDelim, mapkeyDelim, lineDelim这些选项只能用于"textfile" fileFormat。它们定义了如何将带分隔符的文件读入行。

JDBC To Other Databases

Spark SQL 还包括一个数据源,该数据源可以使用 JDBC 从其他数据库读取数据。这个功能应该优先于使用 JdbcRDD。这是因为结果作为 DataFrame 返回,并且可以轻松地在 Spark SQL 中处理或与其他数据源联接。JDBC 数据源也更容易从 Java 或 Python 中使用,因为它不需要用户提供 ClassTag。(注意,这与 Spark SQL JDBC 服务器不同,后者允许其他应用程序使用 Spark SQL 运行查询)。

要开始,您需要在 spark 类路径中包含特定数据库的 JDBC 驱动程序。例如,要从 Spark Shell 连接到 postgres,你需要运行以下命令:

./bin/spark-shell --driver-class-path postgresql-9.4.1207.jar --jars postgresql-9.4.1207.jar

可以使用 Data Sources API 将远程数据库中的表加载为 DataFrame 或 Spark SQL 临时视图。用户可以在数据源选项中指定 JDBC 连接属性。User 和 password 通常作为连接属性提供,用于登录到数据源。除了连接属性,Spark 还支持以下不区分大小写的选项:

Property NameMeaning
url要连接到的 JDBC URL。源特定的连接属性可以在 URL 中指定。例如,jdbc:postgresql://localhost/test?user=fred&password=secret
dbtable应该从中读取或写入的JDBC表。 请注意,在读取路径中使用它时,可以使用在SQL查询的FROM子句中有效的任何东西。 例如,除了完整表之外,您还可以在括号中使用子查询。 不允许同时指定dbtable和查询选项。
query用于将数据读入Spark的查询。指定的查询将用圆括号括起来,用作FROM子句中的子查询。Spark还会为子查询子句分配一个别名。例如,spark将向JDBC Source发出以下形式的查询。
SELECT FROM (<user_specified_query>) spark_gen_alias
下面是使用此选项时的一些限制。
1、不允许同时指定dbtable和query
2、不允许同时指定query和partitionColumn选项。当需要指定partitionColumn选项时,可以使用dbtable选项来指定子查询,并且可以使用作为dbtable一部分提供的子查询别名来限定分区列。
spark.read.format(“jdbc”)
.option(“url”, jdbcUrl)
.option(“query”, “select c1, c2 from t1”)
.load()
driver用于连接到此URL的JDBC驱动程序的类名。
partitionColumn, lowerBound, upperBound如果指定了其中一个选项,则必须全部指定这些选项。此外,必须指定numPartitions。它们描述了当从多个工作器并行读取时如何对表进行分区。partitionColumn必须是相关表中的数字、日期或时间戳列。注意,lowerBound和upperBound仅用于决定分区跨步,而不是用于过滤表中的行。因此,将对表中的所有行进行分区并返回。此选项仅适用于阅读。
numPartitions可用于表读写并行性的最大分区数。这也决定了并发JDBC连接的最大数量。如果要写的分区数量超过了这个限制,我们在写之前通过调用coalesce(numPartitions)将其减少到这个限制。
queryTimeout驱动程序等待Statement对象执行到给定秒数的秒数。0表示没有限制。在写路径中,这个选项取决于JDBC驱动程序如何实现API setQueryTimeout,例如,h2 JDBC驱动程序检查每个查询的超时,而不是整个JDBC批处理。默认值为0。
fetchsizeJDBC获取大小,它决定了每次往返需要获取多少行。这可以帮助JDBC驱动程序的性能默认为低读取大小(例如Oracle的10行)。此选项仅适用于阅读。
batchsizeJDBC批处理大小,它决定了每次往返要插入多少行。这有助于提高JDBC驱动程序的性能。此选项仅适用于写入。默认值是1000。
isolationLevel应用于当前连接的事务隔离级别。它可以是NONE、READ COMMITTED、READ UNCOMMITTED、REPEATABLE READ或SERIALIZABLE中的一个,对应于JDBC的Connection对象定义的标准事务隔离级别,默认为READ UNCOMMITTED。此选项仅适用于写入。请参考java.sql.Connection中的文档。
sessionInitStatement在每个数据库会话打开到远程数据库之后和在开始读取数据之前,他的选项执行自定义SQL语句(or a PL/SQL block)使用它来实现会话初始化代码Example: option("sessionInitStatement", """BEGIN execute immediate 'alter session set "_serial_direct_read"=true'; END;""")
truncate这是一个与JDBC编写器相关的选项。当SaveMode。如果启用了覆盖,则该选项将导致Spark截断现有表,而不是删除并重新创建它。这样可以更高效,并防止删除表元数据(例如,索引)。但是,在某些情况下,例如当新数据具有不同的模式时,它将不起作用。默认值为false。此选项仅适用于写入。
cascadeTruncate这是一个与JDBC编写器相关的选项。如果JDBC数据库(目前PostgreSQL和Oracle)启用并支持此选项,则允许执行TRUNCATE TABLE t CASCADE(在PostgreSQL的情况下,执行TRUNCATE TABLE ONLY t CASCADE以防止无意中截断后代表)。这将影响其他表,因此应该小心使用。此选项仅适用于写入。它默认为JDBC数据库的缺省级联截断行为,在每个JDBCDialect中的isCascadeTruncate中指定。
// Note: JDBC loading and saving can be achieved via either the load/save or jdbc methods
// Loading data from a JDBC source
val jdbcDF = spark.read
  .format("jdbc")
  .option("url", "jdbc:postgresql:dbserver")
  .option("dbtable", "schema.tablename")
  .option("user", "username")
  .option("password", "password")
  .load()

val connectionProperties = new Properties()
connectionProperties.put("user", "username")
connectionProperties.put("password", "password")
val jdbcDF2 = spark.read
  .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)
// Specifying the custom data types of the read schema
connectionProperties.put("customSchema", "id DECIMAL(38, 0), name STRING")
val jdbcDF3 = spark.read
  .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)

// Saving data to a JDBC source
jdbcDF.write
  .format("jdbc")
  .option("url", "jdbc:postgresql:dbserver")
  .option("dbtable", "schema.tablename")
  .option("user", "username")
  .option("password", "password")
  .save()

jdbcDF2.write
  .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)

// Specifying create table column data types on write
jdbcDF.write
  .option("createTableColumnTypes", "name CHAR(64), comments VARCHAR(1024)")
  .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)

Apache Avro Data Source Guide

自从 Spark 2.4发布以来,Spark SQL 提供了读写 Apache Avro 数据的内置支持。

Deploying

spark-avro 模块是外部的,默认情况下不包括spark-submitorspark-shell

与任何 Spark 应用程序一样,Spark-submit 用于启动应用程序。Spark-avro _ 2.12及其依赖项可以直接添加到 spark-submit 使用--packages, such as,

./bin/spark-submit --packages org.apache.spark:spark-avro_2.12:3.1.1 ...

对于在 spark-shell 上进行的实验,还可以使用 – packages 直接添加 org.apache.spark: spark-avro _ 2.12及其依赖项,

./bin/spark-shell --packages org.apache.spark:spark-avro_2.12:3.1.1 ...

有关提交具有外部依赖关系的应用程序的详细信息,请参阅应用程序提交指南

Load and Save Functions

因为 spark-avro 模块是外部的,所以在 DataFrameReader 或 DataFrameWriter 中没有.avro API。

要以 Avro 格式加载/保存数据,需要将 data source 选项格式指定为 Avro (或 org.apache.spark.sql.Avro)。

val usersDF = spark.read.format("avro").load("examples/src/main/resources/users.avro")
usersDF.select("name", "favorite_color").write.format("avro").save("namesAndFavColors.avro")

to_avro() and from_avro()

Avro 包为提供了to_avro来以 Avro 格式编码二进制列的函数,以及from_Avro ()将 Avro 二进制数据解码为一列的函数,这两个函数都将一列转换为另一列,并且输入/输出 SQL 数据类型可以是复杂类型或基元类型。

使用 Avro 记录作为专栏是有用的时候,读或写的流源,如Kafka。每个 Kafka key-value都将增加一些元数据,比如 Kafka 中的摄入时间戳、 Kafka 中的偏移量等。

如果包含数据的“值”字段位于 Avro 中,则可以使用from_avro()提取你的数据,丰富它,清理它,然后把它下沉到kafka或写出一个文件

to_avro()可以用来将结构体变成 Avro 记录。当您希望将多个列重新编码为一个列时,这种方法特别有用,因为这样可以将数据写出 Kafka

import org.apache.spark.sql.avro.functions._

// `from_avro` requires Avro schema in JSON string format.
val jsonFormatSchema = new String(Files.readAllBytes(Paths.get("./examples/src/main/resources/user.avsc")))

val df = spark
  .readStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .option("subscribe", "topic1")
  .load()

// 1. Decode the Avro data into a struct;
// 2. Filter by column `favorite_color`;
// 3. Encode the column `name` in Avro format.
val output = df
  .select(from_avro('value, jsonFormatSchema) as 'user)
  .where("user.favorite_color == \"red\"")
  .select(to_avro($"user.name") as 'value)

val query = output
  .writeStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .option("topic", "topic2")
  .start()

Data Source Option

Avro的数据源选项可以通过

DataFrameReader or DataFrameWriter上的.option方法。

function中的options参数

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值