spark.databricks.cloudFiles.schemaInference.sampleSize.numFiles
默认情况下,自动加载程序架构推理会试图避免由于类型不匹配而出现的架构演变问题。 对于不对数据类型(JSON 和 CSV)进行编码的格式,自动加载程序会将所有列推理为字符串(包括 JSON 文件中的嵌套字段)。 对于具有类型化架构(Parquet 和 Avro)的格式,自动加载程序会采样一部分文件且合并单个文件的架构。 下表汇总了此行为:
默认推理数据类型
Apache Spark DataFrameReader 使用不同的行为进行架构推理,根据示例数据为 JSON 和 CSV 源中的列选择数据类型。 若要使用自动加载程序实现此行为,请将选项 cloudFiles.inferColumnTypes
设置为 true
。
对 CSV 数据的架构进行推理时,自动加载程序会假定文件包含标头。 如果 CSV 文件不包含标头,请提供选项 .option("header", "false")
。 此外,自动加载程序会合并样本中所有文件的架构,以得到一个全局架构。 然后,自动加载程序可以根据文件标头读取每个文件,并正确分析 CSV。
当列在两个 Parquet 文件中具有不同的数据类型时,自动加载程序会尝试将一种类型 upcast
为另一种类型。 如果无法进行向上转换,则数据推理会失败。 有关示例,请参见下表:
向上转换类型
自动加载程序架构演变的工作原理是怎样的?
自动加载程序在处理数据时会检测是否添加了新列。 当自动加载程序检测到新列时,会通过 UnknownFieldException
阻止流。 在流引发此错误之前,自动加载程序会在最新的数据微批上执行架构推理,并通过将新列合并到架构末尾来使用最新架构更新架构位置。 现有列的数据类型将保持不变。
Databricks 建议使用工作流配置自动加载程序流,以便在此类架构更改后自动重启。
自动加载程序支持以下架构演变模式,可以在选项 cloudFiles.schemaEvolutionMode
中设置这些模式:
读取新列的行为
分区对自动加载程序有何作用?
如果数据排布在 Hive 样式分区中,则自动加载程序会尝试从数据的基础目录结构推理分区列。 例如,文件路径 base_path/event=click/date=2021-04-01/f0.json
导致 date
和 event
被推理为分区列。 如果基础目录结构包含有冲突的 Hive 分区或者不包含 Hive 样式分区,则会忽略分区列。
二进制文件 (binaryFile
) 和 text
文件格式具有固定数据架构,但支持分区列推理。 Databricks 建议为这些文件格式设置 cloudFiles.schemaLocation
。 这可以避免任何潜在的错误或信息丢失,并防止在每次自动加载程序启动时进行分区列推理。
不考虑对分区列进行架构演变。 如果有一个类似于 base_path/event=click/date=2021-04-01/f0.json
的初始目录结构,然后开始以 base_path/event=click/date=2021-04-01/hour=01/f1.json
的形式接收新文件,那么自动加载程序将忽略小时列。 若要捕获新分区列的信息,请将 cloudFiles.partitionColumns
设置为 event,date,hour
。
选项 cloudFiles.partitionColumns
采用逗号分隔的列名称列表。 仅分析目录结构中 key=value
格式的列。
什么是补救数据列?
当自动加载程序推理架构时,会自动将补救数据列添加为架构中的 _rescued_data
。 可以重命名该列,或者在提供架构的情况下通过设置选项 rescuedDataColumn
来包含该列。
补救数据列可确保补救与架构不匹配的列,而不是删除这些列。 补救数据列包含出于以下原因而未被分析的所有数据:
架构中缺少该列。
类型不匹配。
案例不匹配。
补救数据列中包含一个 JSON,其中包含已补救列以及记录的源文件路径。
分析记录时,JSON 和 CSV 分析器支持三种模式:PERMISSIVE
、DROPMALFORMED
和 FAILFAST
。 与 rescuedDataColumn
一起使用时,数据类型不匹配不会导致在 DROPMALFORMED
模式下删除记录,或者在 FAILFAST
模式下引发错误。 只有损坏的记录会被删除或引发错误,例如不完整或格式错误的 JSON 或 CSV。 如果在分析 JSON 或 CSV 时使用 badRecordsPath
,则在使用 rescuedDataColumn
时,不会将数据类型不匹配情况视为错误的记录。 只有不完整的和格式错误的 JSON 或 CSV 记录会存储在 badRecordsPath
中。
更改区分大小写的行为
除非启用了区分大小写功能,否则会将列 abc
、Abc
和 ABC
视为相同的列,这样做的目的是进行架构推理。 所选择的大小写是非特定的,具体取决于采样的数据。 可以通过架构提示来强制要求使用哪种大小写。 进行选择并推断架构后,自动加载程序不会考虑那些未进行选择但与架构一致的大小写变体。
启用数据列时,以非架构所用的大小写形式命名的字段将加载到 _rescued_data
列。 通过将选项 readerCaseSensitive
设置为 false 来更改此行为,在这种情况下自动加载程序将以不区分大小写的方式读取数据。
使用架构提示重写架构推理
通过使用架构提示,可以强制实施你所知道的并期望出现在推理架构中的信息。 如果你知道某列采用特定的属性类型,或者想要选择更常规的数据类型(例如不使用 integer
,而改用 double
),可以为列数据类型提供任意数量的提示(格式为符合 SQL 架构规范语法的字符串),例如下文所示:
.option("cloudFiles.schemaHints", "tags map<string,string>, version int")
有关支持的数据类型列表,请参阅有关数据类型的文档。
如果启动流时不存在某个列,则你还可使用架构提示将该列添加到推理的架构中。
以下推理架构示例演示了使用架构提示时的行为。
推理的架构:
|-- date: string
|-- quantity: int
|-- user_info: struct
| |-- id: string
| |-- name: string
| |-- dob: string
|-- purchase_options: struct
| |-- delivery_address: string
指定以下架构提示:
.option("cloudFiles.schemaHints", "date DATE, user_info.dob DATE, purchase_options MAP<STRING,STRING>, time TIMESTAMP")
|-- date: string -> date
|-- quantity: int
|-- user_info: struct
| |-- id: string
| |-- name: string
| |-- dob: string -> date
|-- purchase_options: struct -> map<string,string>
|-- time: timestamp
Databricks Runtime 9.1 LTS 及更高版本支持数组和映射架构提示。
下面是一个具有复杂数据类型的推理架构示例,它演示了使用架构提示时的行为。
推理的架构:
|-- products: array<string>
|-- locations: array<string>
|-- users: array<struct>
| |-- users.element: struct
| | |-- id: string
| | |-- name: string
| | |-- dob: string
|-- ids: map<string,string>
|-- names: map<string,string>
|-- prices: map<string,string>
|-- discounts: map<struct,string>
| |-- discounts.key: struct
| | |-- id: string
| |-- discounts.value: string
|-- descriptions: map<string,struct>
| |-- descriptions.key: string
| |-- descriptions.value: struct
| | |-- content: int
指定以下架构提示:
.option("cloudFiles.schemaHints", "products ARRAY<INT>, locations.element STRING, users.element.id INT, ids MAP<STRING,INT>, names.key INT, prices.value INT, discounts.key.id INT, descriptions.value.content STRING")
|-- products: array<string> -> array<int>
|-- locations: array<int> -> array<string>
|-- users: array<struct>
| |-- users.element: struct
| | |-- id: string -> int
| | |-- name: string
| | |-- dob: string
|-- ids: map<string,string> -> map<string,int>
|-- names: map<string,string> -> map<int,string>
|-- prices: map<string,string> -> map<string,int>
|-- discounts: map<struct,string>
| |-- discounts.key: struct
| | |-- id: string -> int
| |-- discounts.value: string
|-- descriptions: map<string,struct>
| |-- descriptions.key: string
| |-- descriptions.value: struct
| | |-- content: int -> string
仅当未向自动加载程序提供架构时,才会使用架构提示。 不管是启用还是禁用了 cloudFiles.inferColumnTypes
,都可以使用架构提示。