Spark DataFrame可以读取多少种数据?点击这里可看全文
文章目录
概述
Spark SQL支持以下数据源类型:
- CSV文件
- JSON文件
- Parquet文件
- ORC文件
- Avro文件
- 文本文件
- Hive表
- JDBC连接的其他数据库
- Protobuf
- 二进制数据
这些只是一些常见的数据源类型,还有其他第三方库和扩展可以提供更多数据源的支持。
数据源
Spark SQL通过DataFrame接口支持对各种数据源的操作。可以使用关系转换对DataFrame进行操作,还可以将其用于创建临时视图。将DataFrame注册为临时视图允许您在其数据上运行SQL查询。本节介绍了使用Spark数据源加载和保存数据的一般方法,并详细介绍了内置数据源可用的特定选项。
通用加载/保存函数
手动指定选项
您还可以手动指定要使用的数据源以及要传递给数据源的任何额外选项。数据源通过其完全限定名称(即org.apache.spark.sql.parquet)进行指定,但对于内置数据源,您也可以使用它们的简短名称(json、parquet、jdbc、orc、libsvm、csv、text)。从任何数据源类型加载的DataFrame可以使用此语法转换为其他类型。
JSON文件:
val df = spark.read.format("json").load("path/to/file.json")
df.select("name", "age").write.format("parquet").save("path/to/output.parquet")
CSV文件:
val df = spark.read.format("csv")
.option("sep", ";")
.option("inferSchema", "true")
.option("header", "true")
.load("path/to/file.csv")
这些额外选项在写操作期间也会被使用。例如,您可以控制ORC数据源的布隆过滤器和字典编码。以下是一个创建布隆过滤器并仅对favorite_color使用字典编码的ORC示例。对于Parquet,也存在parquet.bloom.filter.enabled和parquet.enable.dictionary选项。有关更详细的有关额外ORC / Parquet选项的信息,请访问官方Apache ORC / Parquet网站。
ORC数据源:
val df = spark.read.orc("path/to/file.orc")
df.write.format("orc")
.option("orc.bloom.filter.columns", "favorite_color")
.option("orc.dictionary.key.threshold", "1.0")
.option("orc.column.encoding.direct", "name")
.save("path/to/output.orc")
Parquet数据源:
val df = spark.read.parquet("path/to/file.parquet")
df.write.format("parquet")
.option("parquet.bloom.filter.enabled#favorite_color", "true")
.option("parquet.bloom.filter.expected.ndv#favorite_color", "1000000")
.option("parquet.enable.dictionary", "true")
.option("parquet.page.write-checksum.enabled", "false")
.save("path/to/output.parquet")
直接在文件上运行SQL
除了使用read API将文件加载到DataFrame并查询它之外,您还可以直接使用SQL查询该文件。
val df = spark.sql("SELECT * FROM parquet.`path/to/file.parquet`")
保存模式
保存操作可选择采用SaveMode,指定如何处理现有数据(如果存在)。重要的是要意识到,这些保存模式不使用任何锁定,并且不是原子的。此外,在执行Overwrite操作时,数据将在写入新数据之前被删除。
Scala/Java | Any Language | 含义 |
---|---|---|
SaveMode.ErrorIfExists | “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。 |
保存到持久表
还可以使用saveAsTable命令将DataFrame保存为Hive metastore中的持久表。请注意,不需要现有的Hive部署即可使用此功能。Spark将为您创建一个默认的本地Hive metastore(使用Derby)。与createOrReplaceTempView命令不同,saveAsTable将实现DataFrame的内容并在Hive metastore中创建一个指向数据的指针。持久表即使在您的Spark程序重新启动后仍然存在,只要您保持与相同metastore的连接。可以通过在SparkSession上调用table方法并指定表名来创建持久表的DataFrame。
对于基于文件的数据源,例如文本、parquet、json等,您可以通过path选项指定自定义表路径,例如df.write.option(“path”, “/some/path”).saveAsTable(“t”)。当删除表时,不会删除自定义表路径,并且表数据仍然存在。如果未指定自定义表路径,则Spark将数据写入仓库目录下的默认表路径。删除表时,也将删除默认表路径。
从Spark 2.1开始,持久数据源表在Hive metastore中存储每个分区的元数据。这带来了几个好处:
- 由于metastore只能返回查询所需的分区,因此不再需要在对表的第一个查询上发现所有分区。
- 对于使用Datasource API创建的表,现在可以使用Hive DDL(如ALTER TABLE PARTITION … SET LOCATION)。
请注意,默认情况下,在创建外部数据源表(具有path选项的表)时不会收集分区信息。要在metastore中同步分区信息,可以调用MSCK REPAIR TABLE。
分桶、排序和分区
对于基于文件的数据源,还可以对输出进行分桶、排序或分区。分桶和排序仅适用于持久表:
df.write.bucketBy(42, "name").sortBy("age").saveAsTable("people_bucketed")
而分区可以在使用Dataset API时与save和saveAsTable一起使用。
df.write.partitionBy("favorite_color").format("parquet").save("namesPartByColor.parquet")
也可以将分区和分桶用于单个表:
val df = spark.read.parquet("path/to/file.parquet")
df.write.partitionBy("favorite_color").bucketBy(42, "name").saveAsTable("users_partitioned_bucketed")
partitionBy将创建一个如“分区发现”部分所述的目录结构。因此,它对高基数列具有有限的适用性。相比之下,bucketBy将数据分布在固定数量的桶中,并且在唯一值的数量无限时可用。
通用文件源选项
通用文件源选项
以下通用选项/配置仅在使用基于文件的数据源时生效:parquet、orc、avro、json、csv、text。
请注意,下面示例中使用的目录层次结构如下:
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’}”)
忽略损坏的文件
Spark允许您使用配置项spark.sql.files.ignoreCorruptFiles
或数据源选项ignoreCorruptFiles
在读取文件时忽略损坏的文件。当设置为true
时,Spark作业将继续运行,并且已读取的内容将被返回。
要在读取数据文件时忽略损坏的文件,可以使用以下方法:
Scala
// 使用数据源选项启用忽略损坏的文件
// dir1/file3.json 在parquet的视图中是损坏的
val testCorruptDF0 = spark.read.option("ignoreCorruptFiles", "true").parquet(
"examples/src/main/resources/dir1/",
"examples/src/main/resources/dir1/dir2/")
testCorruptDF0.show()
// +-------------+
// | file|
// +-------------+
// |file1.parquet|
// |file2.parquet|
// +-------------+
// 使用配置项启用忽略损坏的文件
spark.sql("set spark.sql.files.ignoreCorruptFiles=true")
// dir1/file3.json 在parquet的视图中是损坏的
val testCorruptDF1 = spark.read.parquet(
"examples/src/main/resources/dir1/",
"examples/src/main/resources/dir1/dir2/")
testCorruptDF1.show()
// +-------------+
// | file|
// +-------------+
// |file1.parquet|
// |file2.parquet|
// +-------------+
忽略缺失的文件
Spark允许您使用配置项spark.sql.files.ignoreMissingFiles
或数据源选项ignoreMissingFiles
在读取文件时忽略缺失的文件。这里,缺失的文件指的是在构建DataFrame之后删除的文件。当设置为true
时,Spark作业将继续运行,并且已读取的内容将被返回。
路径Glob过滤器
pathGlobFilter
用于仅包含与模式匹配的文件名匹配的文件。语法遵循org.apache.hadoop.fs.GlobFilter
。它不会改变分区发现的行为。
要加载路径匹配给定Glob模式的文件,并保持分区发现的行为,可以使用以下方法:
Scala
val testGlobFilterDF = spark.read.format("parquet")
.option("pathGlobFilter", "*.parquet") // json文件将被过滤掉
.load("examples/src/main/resources/dir1")
testGlobFilterDF.show()
// +-------------+
// | file|
// +-------------+
// |file1.parquet|
// +-------------+
递归文件查找
recursiveFileLookup
用于递归加载文件,并禁用分区推断。其默认值为false
。如果数据源在recursiveFileLookup
为true
时明确指定了partitionSpec
,将会抛出异常。
要递归加载所有文件,可以使用以下方法:
Scala
val recursiveLoadedDF = spark.read.format("parquet")
.option("recursiveFileLookup", "true")
.load("examples/src/main/resources/dir1")
recursiveLoadedDF.show()
// +-------------+
// | file|
// +-------------+
// |file1.parquet|
// |file2.parquet|
// +-------------+
修改时间路径过滤器
modifiedBefore
和modifiedAfter
是可一起或分别应用的选项,以在Spark批量查询期间更精细地控制加载哪些文件。(请注意,结构化流处理的文件源不支持这些选项。)
modifiedBefore
:可选时间戳,仅包含修改时间在指定时间之前的文件。提供的时间戳必须符合以下格式:YYYY-MM-DDTHH:mm:ss(例如2020-06-01T13:00:00)。
modifiedAfter
:可选时间戳,仅包含修改时间在指定时间之后的文件。提供的时间戳必须符合以下格式:YYYY-MM-DDTHH:mm:ss(例如2020-06-01T13:00:00)。
当未提供时区选项时,时间戳将根据Spark会话的时区(spark.sql.session.timeZone)进行解释。
要加载路径匹配给定修改时间范围的文件,可以使用以下方法:
Scala
val beforeFilterDF = spark.read.format("parquet")
// 仅允许在2020年7月1日05:30之前修改的文件
.option("modifiedBefore", "2020-07-01T05:30:00")
.load("examples/src/main/resources/dir1");
beforeFilterDF.show();
// +-------------+
// | file|
// +-------------+
// |file1.parquet|
// +-------------+
val afterFilterDF = spark.read.format("parquet")
// 仅允许在2020年6月1日05:30之后修改的文件
.option("modifiedAfter", "2020-06-01T05:30:00")
.load("examples/src/main/resources/dir1");
afterFilterDF.show();
// +-------------+
// | file|
// +-------------+
// +-------------+
Parquet文件
Parquet是一种列式存储格式,被许多其他数据处理系统支持。Spark SQL提供了对读写Parquet文件的支持,可以自动保留原始数据的模式。在读取Parquet文件时,为了兼容性考虑,所有列都会自动转换为可为空。
编程加载数据
使用上面示例中的数据:
import spark.implicits._
val peopleDF = spark.read.json("examples/src/main/resources/people.json")
// 可以将DataFrame保存为Parquet文件,并保留模式信息
peopleDF.write.parquet("people.parquet")
// 读取上面创建的Parquet文件
// Parquet文件是自描述的,因此模式会被保留
// 加载Parquet文件的结果也是一个DataFrame
val parquetFileDF = spark.read.parquet("people.parquet")
// Parquet文件还可以用于创建临时视图,然后在SQL语句中使用
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|
// +------------+
分区发现
表分区是Hive等系统中常用的优化方法。在分区表中,数据通常存储在不同的目录中,每个分区目录的路径中编码了分区列的值。所有内置的文件源(包括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
传递给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.parquet
或SparkSession.read.load
,则gender不会被视为分区列。如果用户需要指定分区发现应该从哪个基础路径开始,可以设置数据源选项中的basePath
。例如,当path/to/table/gender=male
是数据的路径,并且用户将basePath
设置为path/to/table/
,则gender将成为分区列。
模式合并
与Protocol Buffer、Avro和Thrift一样,Parquet也支持模式演进。用户可以从简单的模式开始,然后根据需要逐渐添加更多的列到模式中。这样,用户可能会得到具有不同但相互兼容的模式的多个Parquet文件。Parquet数据源现在能够自动检测到这种情况并合并所有这些文件的模式。
由于模式合并是一个相对昂贵的操作,在大多数情况下并非必需,所以从1.5.0版本开始,我们默认关闭了模式合并。可以通过以下方式启用它:
- 在读取Parquet文件时设置数据源选项
mergeSchema
为true
(如下面的示例所示); - 设置全局SQL选项
spark.sql.parquet.mergeSchema
为true
。
// 创建一个简单的DataFrame,并存储到分区目录中
val squaresDF = spark.sparkContext.makeRDD(1 to 5).map(i => (i, i * i)).toDF("value", "square")
squaresDF.write.parquet("data/test_table/key=1")
// 在新的分区目录中创建另一个DataFrame,添加一个新列并删除一个现有列
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")
// 读取分区表
val mergedDF = spark.read.option("mergeSchema", "true").parquet("data/test_table")
mergedDF.printSchema()
// 合并后的模式将包含所有Parquet文件中的3个列,以及分区目录路径中的分区列
// root
// |-- value: int (nullable = true)
// |-- square: int (nullable = true)
// |-- cube: int (nullable = true)
// |-- key: int (nullable = true)
Hive元数据存储的Parquet表转换
当从Hive元数据存储的Parquet表读取并写入非分区的Hive元数据存储的Parquet表时,Spark SQL将尝试使用自己的Parquet支持来替代Hive SerDe,以获得更好的性能。此行为由spark.sql.hive.convertMetastoreParquet
配置项控制,默认启用。
Hive/Parquet模式协调
从表模式处理的角度来看,Hive和Parquet有两个关键区别:
- Hive不区分大小写,而Parquet区分大小写;
- Hive认为所有列都可为空,而在Parquet中,空值的可空性是重要的。
因此,在将Hive元数据存储的Parquet表转换为Spark SQL Parquet表时,我们必须协调Hive元数据模式和Parquet模式。协调规则如下:
- 在两个模式中具有相同名称的字段必须具有相同的数据类型,无论可空性如何。协调后的字段应该具有Parquet一侧的数据类型,以确保可空性得到保留。
- 协调后的模式仅包含Hive元数据存储模式中定义的字段。
- 任何仅出现在Parquet模式中的字段在协调模式中被丢弃。
- 任何仅出现在Hive元数据存储模式中的字段在协调模式中作为可为空字段添加。
元数据刷新
Spark SQL会缓存Parquet元数据以提高性能。当启用了Hive元数据存储的Parquet表转换时,这些转换表的元数据也会被缓存。如果这些表被Hive或其他外部工具更新,您需要手动刷新它们以确保一致的元数据。
spark.catalog.refreshTable("my_table")
列式加密
从Spark 3.2开始,支持对具有Apache Parquet 1.12+的Parquet表进行列式加密。
Parquet使用了包络加密的方法,其中文件部分使用“数据加密密钥”(DEK)进行加密,而DEK则使用“主加密密钥”(MEK)进行加密。对于每个加密文件/列,Parquet会为其生成一个随机的DEK。MEK是在用户选择的密钥管理服务(KMS)中生成、存储和管理的。Parquet Maven仓库提供了一个带有模拟KMS实现的JAR包,可以在只使用spark-shell的情况下运行列加密和解密,无需部署KMS服务器(下载parquet-hadoop-tests.jar文件并将其放置在Spark jars文件夹中):
sc.hadoopConfiguration.set("parquet.encryption.kms.client.class",
"org.apache.parquet.crypto.keytools.mocks.InMemoryKMS")
// 显式的主密钥(Base64编码),仅在模拟InMemoryKMS时需要
sc.hadoopConfiguration.set("parquet.encryption.key.list",
"keyA:AAECAwQFBgcICQoLDA0ODw== , keyB:AAECAAECAAECAAECAAECAA==")
// 激活Parquet加密,由Hadoop属性驱动
sc.hadoopConfiguration.set("parquet.crypto.factory.class",
"org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory")
// 写入加密的DataFrame文件
// 列"square"将使用主密钥"keyA"进行保护
// Parquet文件页脚将使用主密钥"keyB"进行保护
squaresDF.write.
option("parquet.encryption.column.keys", "keyA:square").
option("parquet.encryption.footer.key", "keyB").
parquet("/path/to/table.parquet.encrypted")
// 读取加密的DataFrame文件
val df2 = spark.read.parquet("/path/to/table.parquet.encrypted")
KMS客户端
InMemoryKMS类仅用于演示和说明Parquet加密功能,不应在实际部署中使用。主加密密钥必须在用户组织中的生产级KMS系统中保存和管理。使用Spark的Parquet加密需要为KMS服务器实现一个客户端类。Parquet提供了用于开发此类的插件接口:
public interface KmsClient {
// 使用主密钥对密钥进行包装(用主密钥进行加密)
public String wrapKey(byte[] keyBytes, String masterKeyIdentifier);
// 使用主密钥对密钥进行解包(用主密钥进行解密)
public byte[] unwrapKey(String wrappedKey, String masterKeyIdentifier);
// 初始化参数的使用是可选的
public void initialize(Configuration configuration, String kmsInstanceID,
String kmsInstanceURL, String accessToken);
}
在parquet-mr存储库中可以找到用于开源KMS的此类示例。生产级KMS客户端应该与组织的安全管理员合作设计,并由具有访问控制管理经验的开发人员构建。创建此类后,可以通过parquet.encryption.kms.client.class
参数传递给应用程序,并且可以像上面示例中展示的那样由一般的Spark用户使用。
注意:默认情况下,Parquet实现了“双封装加密”模式,最大程度地减少了Spark执行器与KMS服务器的交互。在这种模式下,DEK使用“密钥加密密钥”(KEK)进行加密,KEK使用KMS中的MEK进行加密。结果和KEK本身都被缓存在Spark执行器内存中。如果希望使用常规的封装加密,可以将parquet.encryption.double.wrapping
参数设置为false。有关Parquet加密参数的更多详细信息,请访问parquet-hadoop配置页面。
数据源选项
可以通过以下方式设置Parquet的数据源选项:
- 使用DataFrameReader、DataFrameWriter、DataStreamReader或DataStreamWriter的
.option/.options
方法; - 在CREATE TABLE USING DATA_SOURCE中使用OPTIONS子句。
属性名 | 默认值 | 含义 | 作用域 |
---|---|---|---|
datetimeRebaseMode | (spark.sql.parquet.datetimeRebaseModeInRead配置的值) | datetimeRebaseMode选项允许指定DATE、TIMESTAMP_MILLIS、TIMESTAMP_MICROS等逻辑类型的值从儒略历转换为公历的模式。当前支持的模式有:EXCEPTION: 在读取具有双重历法的古老日期/时间戳时会失败。CORRECTED: 以不进行转换的方式加载日期/时间戳。LEGACY: 在读取Parquet文件时将从旧的混合(儒略 + 公历)历法转换为公历历法。 | read |
int96RebaseMode | (spark.sql.parquet.int96RebaseModeInRead配置的值) | int96RebaseMode选项允许指定INT96时间戳从儒略历转换为公历的模式。当前支持的模式有:EXCEPTION: 在读取古老的INT96时间戳时会失败。CORRECTED: 以不进行转换的方式加载INT96时间戳。LEGACY: 在读取Parquet文件时将从旧的混合(儒略 + 公历)历法转换为公历历法。 | read |
mergeSchema | (spark.sql.parquet.mergeSchema配置的值) | 设置是否应该合并从所有Parquet部分文件收集到的模式。这将覆盖spark.sql.parquet.mergeSchema。 | read |
compression | snappy | 保存为文件时使用的压缩编解码器。可以是已知的大小写不敏感的简写名称之一(none、uncompressed、snappy、gzip、lzo、brotli、lz4和zstd)。这将覆盖spark.sql.parquet.compression.codec。 | write |
其他通用选项可以在通用文件源选项中找到。
配置
可以使用SparkSession的setConf
方法进行Parquet的配置,或者通过SQL语句运行SET key=value
命令进行配置。
属性名 | 默认值 | 含义 | 版本 |
---|---|---|---|
spark.sql.parquet.binaryAsString | false | 一些其他的Parquet生成系统(例如Impala、Hive和早期版本的Spark SQL)在写出Parquet模式时不区分二进制数据和字符串。该标志告诉Spark SQL将二进制数据解释为字符串,以与这些系统兼容。 | 1.1.1 |
spark.sql.parquet.int96AsTimestamp | true | 一些Parquet生成系统(特别是Impala和Hive)将Timestamp存储为INT96。该标志告诉Spark SQL将INT96数据解释为Timestamp,以与这些系统兼容。 | 1.3.0 |
spark.sql.parquet.int96TimestampConversion | false | 控制是否应该对由Impala编写的INT96数据应用时间戳调整,这是因为Impala将INT96数据与Hive和Spark的时区偏移不同地存储。 | 2.3.0 |
spark.sql.parquet.outputTimestampType | INT96 | 设置Spark将数据写入Parquet文件时使用的Parquet时间戳类型。INT96是Parquet中一种非标准但常用的时间戳类型。TIMESTAMP_MICROS是Parquet中的标准时间戳类型,它存储了从Unix纪元以来的微秒数。TIMESTAMP_MILLIS也是标准的,但精度为毫秒,这意味着Spark必须截断其时间戳值的微秒部分。 | 2.3.0 |
spark.sql.parquet.compression.codec | snappy | 写入Parquet文件时使用的压缩编解码器。如果在表特定的选项/属性中指定了compression或parquet.compression,则优先级将为compression,parquet.compression,spark.sql.parquet.compression.codec。可以接受的值包括:none、uncompressed、snappy、gzip、lzo、brotli、lz4、zstd。请注意,brotli需要安装BrotliCodec。 | 1.1.1 |
spark.sql.parquet.filterPushdown | true | 启用Parquet的过滤器下推优化时设置为true。 | 1.2.0 |
spark.sql.parquet.aggregatePushdown | false | 如果为true,则会对Parquet进行优化以使聚合操作下推。支持MIN、MAX和COUNT作为聚合表达式。对于MIN/MAX,支持布尔、整数、浮点和日期类型。对于COUNT,支持所有数据类型。如果任何Parquet文件页脚中缺少统计信息,将抛出异常。 | 3.3.0 |
spark.sql.hive.convertMetastoreParquet | true | 当设置为false时,Spark SQL将使用Hive SerDe而不是内置支持来处理Parquet表。 | 1.1.1 |
spark.sql.parquet.mergeSchema | false | 当为true时,Parquet数据源将合并从所有数据文件收集到的模式,否则将从摘要文件中选择模式,如果没有摘要文件,则选择一个随机数据文件。 | 1.5.0 |
spark.sql.parquet.respectSummaryFiles | false | 当为true时,我们假设所有的Parquet分部文件与摘要文件一致,当合并模式时会忽略它们。否则,如果设置为默认值false,我们将合并所有的分部文件。这应该被视为专家级选项,在确切了解其含义之前不应启用。 | 1.5.0 |
spark.sql.parquet.writeLegacyFormat | false | 如果为true,则数据将以Spark 1.4及更早版本的方式写入。例如,十进制值将以Apache Parquet的固定长度字节数组格式写入,而其他系统(如Apache Hive和Apache Impala)使用此格式。如果为false,则使用Parquet中的新格式。例如,十进制将以基于int的格式写入。如果Parquet输出用于不支持此新格式的系统,请将其设置为true。 | 1.6.0 |
spark.sql.parquet.enableVectorizedReader | true | 启用矢量化的Parquet解码。 | 2.0.0 |
spark.sql.parquet.enableNestedColumnVectorizedReader | true | 启用嵌套列(例如struct、list、map)的矢量化Parquet解码。需要启用spark.sql.parquet.enableVectorizedReader。 | 3.3.0 |
spark.sql.parquet.recordLevelFilter.enabled | false | 如果为true,启用基于记录级过滤器的Parquet本机记录过滤器,使用下推下来的过滤器。此配置只在启用了spark.sql.parquet.filterPushdown并且未使用矢量化读取器时生效。您可以通过将spark.sql.parquet.enableVectorizedReader设置为false来确保不使用矢量化读取器。 | 2.3.0 |
spark.sql.parquet.columnarReaderBatchSize | 4096 | 在Parquet矢量化读取器批处理中包含的行数。应该谨慎选择该数字,以最小化开销并避免在读取数据时出现OOM错误。 | 2.4.0 |
spark.sql.parquet.fieldId.write.enabled | true | 字段ID是Parquet模式规范的一种本地字段。启用后,Parquet编写器将Spark模式中的字段ID元数据(如果存在)填充到Parquet模式中。 | 3.3.0 |
spark.sql.parquet.fieldId.read.enabled | false | 字段ID是Parquet模式规范的一种本地字段。启用后,Parquet读取器将使用请求的Spark模式中的字段ID(如果存在)查找Parquet字段,而不是使用列名。 | 3.3.0 |
spark.sql.parquet.fieldId.read.ignoreMissing | false | 当Parquet文件没有任何字段ID但Spark读取模式使用字段ID进行读取时,如果启用此标志,我们将静默返回null,否则将抛出错误。 |