Spark SQL 教程翻译(三) Data Sources

文章目录

  • Data Sources
    • Generic Load/Save Functions
      • Manually Specifying Options
      • Run SQL on files directly
      • Save Modes
      • Saving to Persistent Tables
      • Bucketing, Sorting and Partitioning
    • Parquet Files
      • Loading Data Programmatically
      • Partition Discovery
      • Schema Merging
      • Hive metastore Parquet table conversion
        • Hive/Parquet Schema Reconciliation
        • Metadata Refreshing
      • Configuration
    • ORC Files
    • JSON Datasets
    • Hive Tables
      • Specifying storage format for Hive tables
      • Interacting with Different Versions of Hive Metastore
    • JDBC To Other Databases
    • Troubleshooting

Data Sources

Spark SQL 支持通过 DataFrame 接口对多种数据源进行操作。 可以使用关系转换对 DataFrame 进行操作,也可以用于创建临时视图。 将 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)。 从任何数据源类型加载的 DataFrame 都可以使用此语法转换为其他类型。

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

To load a CSV file you can use:

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

Run SQL on files directly

除了使用读取 API 将文件加载到 DataFrame 并进行查询之外,您还可以直接使用 SQL 查询该文件。

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

Save Modes

保存操作可以选择采用 SaveMode,它指定如何处理现有数据(如果存在)。 重要的是要意识到这些保存模式不使用任何锁定并且不是原子的。 此外,执行覆盖时,数据将在写出新数据之前被删除(先删除 后写入的方式)。

Scala/Java Any Language Meaning
SaveMode.ErrorIfExists (default) "error" or "errorifexists" (default) 将DataFrame保存到数据源时,如果数据已经存在,预计会抛出异常。
SaveMode.Append "append" 将 DataFrame 保存到数据源时,如果数据/表已存在,则希望将 DataFrame 的内容附加到现有数据中。
SaveMode.Overwrite "overwrite" 将 DataFrame 保存到数据源时,如果数据/表已存在,则希望将 DataFrame 的内容附加到现有数据中。
SaveMode.Ignore "ignore" 忽略模式是指在将DataFrame 保存到数据源时,如果数据已经存在,则保存操作预计不会保存DataFrame 的内容,也不会更改现有数据。 这类似于 SQL 中的CREATE TABLE IF NOT EXISTS

Saving to Persistent Tables

也可以使用 saveAsTable 命令将数据帧作为持久表保存到 Hive 元存储中。 请注意,使用此功能不需要现有的 Hive 部署。 Spark 将为您创建一个默认的本地 Hive 元存储(使用 Derby)。 与 createOrReplaceTempView 命令不同,saveAsTable 将具体化 DataFrame 的内容并创建一个指向 Hive 元存储中数据的指针。 即使您的 Spark 程序重新启动,持久表仍然存在,只要您保持与同一个元存储的连接。 可以通过使用表的名称调用 SparkSession 上的 table 方法来创建持久表的 DataFrame。

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

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

  • 由于 Metastore 可以仅返回查询所需的分区,因此不再需要在对表的第一个查询中发现所有分区。
  • ALTER TABLE PARTITION … SET LOCATION 等 Hive DDL 现在可用于使用数据源 API 创建的表。

请注意,在创建外部数据源表(具有路径选项的表)时,默认情况下不会收集分区信息。 要同步 Metastore 中的分区信息,您可以调用 MSCK REPAIR TABLE

Bucketing, Sorting and Partitioning

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

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

当使用数据集 API 时,分区可以与 savesaveAsTable 一起使用。

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

可以对单个表同时使用分区和分桶:

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

partitionBy 创建一个目录结构,如 Partition Discovery 部分所述。 因此,它对具有高基数的列的适用性有限。 相比之下,bucketBy将数据分布在固定数量的存储桶中,并且可以在多个唯一值无界时使用。

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 等系统中常用的优化方法。 在分区表中,数据通常存储在不同的目录中,分区列值编码在每个分区目录的路径中。 所有内置文件源(包括 Text/CSV/JSON/ORC/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,Spark SQL 将自动从路径中提取分区信息。 现在返回的 DataFrame 的模式变为:

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

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

从 Spark 1.6.0 开始,分区发现默认只查找给定路径下的分区。对于上面的示例,如果用户将 path/to/table/gender=male 传递给 SparkSession.read.parquetSparkSession.read.load,则性别不会被视为分区列。如果用户需要指定开始分区发现的基本路径,可以在数据源选项中设置basePath。比如path/to/table/gender=male是数据的路径,用户设置basePath为path/to/table/时`,gender就是一个分区列。

Schema Merging

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

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

  1. 读取 Parquet 文件时将数据源选项 mergeSchema设置为true(如下例所示),
  2. 将全局 SQL 选项 spark.sql.parquet.mergeSchema 设置为 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 元存储 Parquet 表时,Spark SQL 将尝试使用其自己的 Parquet 支持而不是 Hive SerDe 以获得更好的性能。 此行为由 spark.sql.hive.convertMetastoreParquet 配置控制,默认开启。

Hive/Parquet Schema Reconciliation

从表模式处理的角度来看,Hive 和 Parquet 之间有两个主要区别。

  1. Hive 不区分大小写,而 Parquet 不区分大小写
  2. Hive 认为所有列都可以为空,而 Parquet 中的可空性很重要

由于这个原因,在将 Hive Metastore Parquet 表转换为 Spark SQL Parquet 表时,我们必须协调 Hive Metastore 模式与 Parquet 模式。 对账规则如下:

  1. 无论是否为空,在两个架构中具有相同名称的字段都必须具有相同的数据类型。 Reconciled 字段应该具有 Parquet 端的数据类型,以便考虑可空性。

  2. 协调的架构包含在 Hive 元存储架构中定义的那些字段。

    • 仅出现在 Parquet 模式中的任何字段都将被删除到协调模式中。
    • 仅出现在 Hive 元存储架构中的任何字段都添加为协调架构中的可为空字段。
Metadata Refreshing

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

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

Configuration

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

Property Name Default Meaning
spark.sql.parquet.binaryAsString false Some other Parquet-producing systems, in particular Impala, Hive, and older versions of Spark SQL, do not differentiate between binary data and strings when writing out the Parquet schema. This flag tells Spark SQL to interpret binary data as a string to provide compatibility with these systems.
spark.sql.parquet.int96AsTimestamp true Some Parquet-producing systems, in particular Impala and Hive, store Timestamp into INT96. This flag tells Spark SQL to interpret INT96 data as a timestamp to provide compatibility with these systems.
spark.sql.parquet.compression.codec snappy Sets the compression codec used when writing Parquet files. If either compression or parquet.compression is specified in the table-specific options/properties, the precedence would be compression, parquet.compression, spark.sql.parquet.compression.codec. Acceptable values include: none, uncompressed, snappy, gzip, lzo.
spark.sql.parquet.filterPushdown true Enables Parquet filter push-down optimization when set to true.
spark.sql.hive.convertMetastoreParquet true When set to false, Spark SQL will use the Hive SerDe for parquet tables instead of the built in support.
spark.sql.parquet.mergeSchema false When true, the Parquet data source merges schemas collected from all data files, otherwise the schema is picked from the summary file or a random data file if no summary file is available.

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 Name Default Meaning
spark.sql.orc.impl hive The name of ORC implementation. It can be one of native and hive. native means the native ORC support that is built on Apache ORC 1.4.1. hive means the ORC library in Hive 1.2.1.
spark.sql.orc.enableVectorizedReader true Enables vectorized orc decoding in native implementation. If false, a new non-vectorized ORC reader is used in native implementation. For hive implementation, this is ignored.

JSON Datasets

Spark SQL 可以自动推断 JSON 数据集的模式并将其作为 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.xmlcore-site.xml(用于安全配置)和 hdfs-site.xml(用于 HDFS 配置)文件放在 conf/ 中来完成的。

使用 Hive 时,必须使用 Hive 支持实例化 SparkSession,包括与持久 Hive 元存储的连接、对 Hive serdes 的支持和 Hive 用户定义函数。没有现有 Hive 部署的用户仍然可以启用 Hive 支持。 hive-site.xml没有配置时,context会在当前目录自动创建metastore_db,并创建spark.sql.warehouse.dir配置的目录,默认为Spark应用当前目录下的目录spark-warehouse开始了。请注意,自 Spark 2.0.0 起,hive-site.xml 中的 hive.metastore.warehouse.dir 属性已弃用。相反,使用spark.sql.warehouse.dir指定数据库在仓库中的默认位置。您可能需要向启动 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_ints(key int) STORED AS PARQUET LOCATION '$dataDir'")
// The Hive external table should already have data
sql("SELECT * FROM hive_ints").show()
// +---+
// |key|
// +---+
// |  0|
// |  1|
// |  2|
// ...

// 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 表时,你需要定义这个表应该如何从/向文件系统读/写数据,即“输入格式”和“输出格式”。 您还需要定义该表应如何将数据反序列化为行,或将行序列化为数据,即“serde”。 以下选项可用于指定存储格式(“serde”、“输入格式”、“输出格式”),例如CREATE TABLE src(id int) USING hive OPTIONS(fileFormat 'parquet')。 默认情况下,我们将以纯文本形式读取表文件。 请注意,创建表时尚不支持 Hive 存储处理程序,您可以在 Hive 端使用存储处理程序创建表,并使用 Spark SQL 读取它。

Property Name Meaning
fileFormat fileFormat 是一种存储格式规范的包,包括“serde”、“输入格式”和“输出格式”。 目前我们支持 6 种文件格式:‘sequencefile’、‘rcfile’、‘orc’、‘parquet’、‘textfile’ 和 ‘avro’。
inputFormat, outputFormat 这 2 个选项将相应的 InputFormatOutputFormat 类的名称指定为字符串文字,例如 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”文件格式一起使用。 它们定义了如何将分隔文件读入行。

使用 OPTIONS 定义的所有其他属性将被视为 Hive serde 属性。

Interacting with Different Versions of Hive Metastore

Spark SQL 的 Hive 支持最重要的部分之一是与 Hive 元存储的交互,这使 Spark SQL 能够访问 Hive 表的元数据。 从 Spark 1.4.0 开始,Spark SQL 的单个二进制构建可用于查询不同版本的 Hive 元存储,使用如下所述的配置。 请注意,独立于用于与 Metastore 对话的 Hive 版本,Spark SQL 在内部将针对 Hive 1.2.1 进行编译,并使用这些类进行内部执行(serdes、UDF、UDAF 等)。

以下选项可用于配置用于检索元数据的 Hive 版本:

Property Name Default Meaning
spark.sql.hive.metastore.version 1.2.1 Hive 元存储的版本。 可用选项是0.12.01.2.1
spark.sql.hive.metastore.jars builtin 应该用于实例化 HiveMetastoreClient 的 jar 的位置。 此属性可以是以下三个选项之一:builtinUse Hive 1.2.1,当启用 -Phive 时,它与 Spark 程序集捆绑在一起。 选择此选项时,spark.sql.hive.metastore.version 必须为 1.2.1 或未定义。maven使用从 Maven 存储库下载的指定版本的 Hive jar。 通常不建议将此配置用于生产部署。JVM 标准格式的类路径。 该类路径必须包含所有 Hive 及其依赖项,包括正确版本的 Hadoop。 这些 jars 只需要出现在驱动程序上,但是如果您在纱线集群模式下运行,那么您必须确保它们与您的应用程序打包在一起。
spark.sql.hive.metastore.sharedPrefixes com.mysql.jdbc,org.postgresql,com.microsoft.sqlserver,oracle.jdbc 应使用在 Spark SQL 和特定版本的 Hive 之间共享的类加载器加载的类前缀的逗号分隔列表。 应该共享的类的一个示例是与 Metastore 对话所需的 JDBC 驱动程序。 其他需要共享的类是那些与已经共享的类交互的类。 例如,log4j 使用的自定义 appender。
spark.sql.hive.metastore.barrierPrefixes (empty) 应该为 Spark SQL 与之通信的每个 Hive 版本显式重新加载的类前缀的逗号分隔列表。 例如,在通常会共享的前缀中声明的 Hive UDF(即org.apache.spark.*)。

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

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

Property Name Meaning
url 要连接到的 JDBC URL。 可以在 URL 中指定特定于源的连接属性。e.g., jdbc:postgresql://localhost/test?user=fred&password=secret
dbtable 应该读取的 JDBC 表。 请注意,可以使用在 SQL 查询的FROM子句中有效的任何内容。 例如,您还可以使用括号中的子查询来代替完整的表。
driver 用于连接到此 URL 的 JDBC 驱动程序的类名。
partitionColumn, lowerBound, upperBound 如果指定了其中任何选项,则必须全部指定这些选项。 此外,必须指定numPartitions。 他们描述了从多个workers并行读取时如何对表进行分区。 partitionColumn 必须是相关表中的数字列。 请注意,lowerBoundupperBound 仅用于决定分区步长,而不是用于过滤表中的行。 所以表中的所有行都将被分区并返回。 此选项仅适用于阅读。
numPartitions 可用于表读写并行的最大分区数。 这也决定了并发 JDBC 连接的最大数量。 如果要写入的分区数超过此限制,我们会在写入前调用 coalesce(numPartitions) 将其减少到此限制。
fetchsize JDBC 提取大小,它确定每次往返要提取多少行。 这有助于提高 JDBC 驱动程序的性能,这些驱动程序默认为低提取大小(例如,具有 10 行的 Oracle)。 此选项仅适用于阅读。
batchsize JDBC 批处理大小,它确定每次往返插入的行数。 这有助于提高 JDBC 驱动程序的性能。 此选项仅适用于写入。 它默认为“1000”。
isolationLevel 事务隔离级别,适用于当前连接。 它可以是 NONEREAD_COMMITTEDREAD_UNCOMMITTEDREPEATABLE_READSERIALIZABLE 之一,对应于 JDBC Connection 对象定义的标准事务隔离级别,默认为 READ_UNCOMMITTED。 此选项仅适用于写入。 请参考 java.sql.Connection 中的文档。
sessionInitStatement 在每个数据库会话打开到远程 DB 之后和开始读取数据之前,此选项执行自定义 SQL 语句(或 PL/SQL 块)。 使用它来实现会话初始化代码。 示例:option("sessionInitStatement", """BEGIN execute quiet 'alter session set "_serial_direct_read"=true'; END;""")
truncate 这是一个与 JDBC 编写器相关的选项。 启用SaveMode.Overwrite时,此选项会导致 Spark 截断现有表,而不是删除并重新创建它。 这可以更有效,并防止表元数据(例如,索引)被删除。 但是,它在某些情况下不起作用,例如当新数据具有不同的架构时。 它默认为false。 此选项仅适用于写入。
createTableOptions 这是一个与 JDBC 编写器相关的选项。 如果指定,此选项允许在创建表时设置特定于数据库的表和分区选项(例如,CREATE TABLE t (name string) ENGINE=InnoDB.)。 此选项仅适用于写入。
createTableColumnTypes 创建表时要使用的数据库列数据类型而不是默认值。 数据类型信息应以与 CREATE TABLE 列语法相同的格式指定(例如:"name CHAR(64), comments VARCHAR(1024)")。 指定的类型应该是有效的 spark sql 数据类型。 此选项仅适用于写入。
customSchema 用于从 JDBC 连接器读取数据的自定义架构。 例如,"id DECIMAL(38, 0), name STRING"。 您也可以指定部分字段,其他字段使用默认类型映射。 例如,"id DECIMAL(38, 0)"。 列名应与JDBC 表对应的列名一致。 用户可以指定 Spark SQL 的对应数据类型,而不是使用默认值。 此选项仅适用于阅读。
// 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)

Troubleshooting

  • JDBC 驱动程序类必须对客户端会话和所有执行程序上的原始类加载器可见。 这是因为 Java 的 DriverManager 类进行了一项安全检查,导致它在打开连接时忽略原始类加载器不可见的所有驱动程序。 一种方便的方法是修改所有工作节点上的 compute_classpath.sh 以包含您的驱动程序 JAR。

  • 某些数据库,例如 H2,将所有名称都转换为大写。 您需要使用大写字母来引用 Spark SQL 中的这些名称。

你可能感兴趣的:(Spark,翻译,大数据,spark)