如何防止在 AWS Glue 爬网程序运行期间创建多个表?
AWS Glue 爬网程序正在从我的源数据创建多个表。我想知道我该如何做才能防止这种情况继续发生。
简短描述
在第一次 AWS Glue 爬网程序运行期间,爬网程序会读取前 1000 条记录或每个文件的前一兆字节来推断架构。爬网程序读取的数据量取决于文件格式和有效记录的可用性。
例如,如果输入的是 JSON 文件,则爬网程序会读取该文件的前 1 MB。如果爬网程序在文件的前 1 MB 内读取到有效记录,则爬网程序会推断出架构。如果爬网程序无法推断出架构,则它会以 1 MB 的增量读取该文件,最多读取 10 MB。
对于.csv 文件,爬网程序会读取前 1000 条记录或前 1 MB 的数据,以先到者为准。对于 Parquet 文件,爬网程序直接从文件中推断架构。
爬网程序会将从所有子文件夹和文件中推断出的架构进行比较,然后创建表。
当您的源数据文件中的以下参数不使用这些相同配置时,爬网程序会创建多个表:
- 格式,例如 .csv、Parquet 或 JSON
- 压缩类型,例如 SNAPPY、gzip 或 bzip2
- 架构
- Amazon Simple Storage Service (Amazon S3) 分区的结构
解决方法
检查爬网程序日志
要确定导致爬网程序创建多个表的文件,请完成以下步骤:
- 打开 AWS Glue 控制台。
- 在导航窗格中,选择 Crawlers(爬网程序)。
- 选择要检查的爬网程序。
- 选择 Logs(日志)链接,以在 Amazon CloudWatch 控制台上查看日志。
如果 AWS Glue 在上一次爬网程序运行期间创建了多个表,则日志将包括与以下内容类似的条目:
[439d6bb5-ce7b-4fb7-9b4d-805346a37f88] INFO : Created table
2_part_00000_24cab769_750d_4ef0_9663_0cc6228ac858_c000_snappy_parquet in
database glue
[439d6bb5-ce7b-4fb7-9b4d-805346a37f88] INFO : Created table
2_part_00000_3518b196_caf5_481f_ba4f_3e968cbbdd67_c000_snappy_parquet in
database glue
[439d6bb5-ce7b-4fb7-9b4d-805346a37f88] INFO : Created table
2_part_00000_6d2fffc2_a893_4531_89fa_72c6224bb2d6_c000_snappy_parquet in
database glue
日志条目包括导致爬网程序创建多个表的文件的名称。
防止创建多个表
为防止创建多个表,请针对您的用例执行以下操作之一。
确认数据文件使用相同的架构、格式和压缩类型
在某些情况下,文件使用不同的架构。例如,使用架构 A 时字段 X 为 INT 类型,使用架构 B 时字段 X 为 BOOL 类型。
对于此用例,请使用 from_options 函数运行 AWS Glue 提取、转换、加载 (ETL) 作业来读取异常值数据。然后,在您的源中将异常值数据类型转换为正确或最常见的数据类型。
您也可以使用现有表 DDL 在 Amazon Athena 中手动创建表。然后,运行 AWS Glue 爬网程序来更新表元数据。配置爬网程序,使其不会覆盖表的现有架构。
创建爬网程序时合并兼容的架构
爬网程序在文件夹级别推断架构,并会比较所有文件夹的架构。爬网程序会检查这些架构是否匹配以及分区阈值是否高于 70%。如果匹配,则会将这些架构表示为表的分区。如果不匹配,则爬网程序会为每个文件夹创建一个表。这会导致表的数量增加。
您的数据在某些输入文件中可能有不同的架构,而在其他文件中可能有类似的架构。创建爬网程序时,请合并兼容的架构。
在 AWS Glue 控制台的 Configure the crawler's output(配置爬网程序输出)页面上,在 Grouping behavior for S3 data (optional)(S3 数据的分组行为(可选))下,选择 Create a single schema for each S3 path(为每条 S3 路径创建单个架构)。如果数据兼容,则爬网程序在评估路径中的 Amazon S3 对象时会忽略架构相似性。
有关详细信息,请参阅为每条 Amazon S3 包含路径创建单个架构。
检查您的输入文件是否有不同的 Amazon S3 路径
当 Amazon S3 前缀内的结构不一致时,爬网程序会将每个路径假定为一个单独的表。然后,爬网程序会创建多个表。如果您的输入文件具有不同的 Amazon S3 结构或路径,则默认情况下,爬网程序会创建多个表。
例如,如果您在 s3://doc-example-bucket/doc-example-key/doc-example-table Amazon S3 路径上运行爬网程序,该路径的分区结构类似于以下:
- s3://doc-example-bucket/doc-example-key/doc-example-table/dt=2020-08-20/doc-example-file1.csv
- s3://doc-example-bucket/doc-example-key/doc-example-table/dt=2020-08-21/dox-example-file2.csv
- s3://doc-example-bucket/doc-example-key/doc-example-table/dt=2020-08-22/doc-example-file3.csv
那么请将以下文件添加到前三个文件中:
- s3://doc-example-bucket/doc-example-key/doc-example-table/dox-example-file4.csv
- s3:// doc-example-bucket/doc-example-key/doc-example-table/doc-example-file5.csv
由于文件夹分区结构不一致,爬网程序会在另一次爬网程序运行中创建五个单独的表。
为避免出现此问题,请手动或以编程方式确认架构是否一致。在前面的示例中,您可以删除不含 dt=xxxx-xx-xx 分区的 Amazon S3 文件,也可以为文件 doc-example-file4.csv 和 doc-example-file5.csv 添加分区。要排除不需要的文件或文件夹,请使用排除模式。
使用一致的标头
使用 .csv 格式的数据时,请始终使用标头。如果您的某些文件具有标头而其他文件没有,则爬网程序会创建多个表。
删除爬网程序运行创建的多个表
从 CloudWatch 日志的爬网程序运行中筛选表。然后,调用 BatchDeleteTable API 来删除这些表。
完成以下步骤:
- 打开 AWS Glue 控制台。
- 在导航窗格中,选择 Crawlers(爬网程序)。
- 在 Crawler runs(爬网程序运行)下,选择目标爬网程序名称。
- 选择 View run details(查看运行详细信息)。
- 在 Crawler run details(爬网程序运行详细信息)页面上,记下要在 BatchDeleteTable 调用中使用的爬网程序运行的 ID。
- 选择 CloudWatch logs(CloudWatch 日志),然后选择 /aws-glue/crawlers 日志组。
- 选择 Actions(操作),然后选择 Export data to Amazon S3(将数据导出到 Amazon S3)。
- 输入以下信息以导出数据:
对于 time range(时间范围),请使用爬网程序运行的开始和结束时间。
对于 stream prefix(流前缀),请添加爬网程序名称。
选择您的目标 S3 存储桶和前缀以导出爬网程序运行日志。 - 在 /aws-glue/crawlers 日志组中,选择 Actions(操作),然后选择 View all exports to Amazon S3(查看导出到 Amazon S3 的所有数据)。
- 为 /aws-glue/crawlers 日志组和目标 S3 存储桶选择导出任务。
- 选择 View in Amazon S3(在 Amazon S3 中查看)。
- 在 Amazon S3 控制台上,选择具有目标爬网程序名称的文件夹。
- 将导出的.gz 格式的日志文件下载到本地计算机或 Amazon Elastic Compute Cloud (Amazon EC2) 实例。然后,将文件解压缩到本地路径。记下路径位置,以在 BatchDeleteTable 调用中使用。
- 要筛选表,请从本地计算机或 EC2 实例扫描解压缩的文件。
- 调用 AWS Glue BatchDeleteTable API 来删除这些表。
示例脚本:
import boto3 import re # input parameters catalogId='CatalogId' database = 'databaseName' crawlId= 'crawlRunId' logFilePath = 'local_decompressed_file_path' # function to extract the created tables by the given crawl run id in target database def extract_table_info(file_path, target_crawl_id, target_database): list_tables = [] with open(file_path, 'r') as file: for line in file: match = re.search(f'.*\\[{target_crawl_id}\\] INFO : Created table (\\w+(?:_[a-f0-9]+)*) in database {target_database}', line) if match: table_name = match.group(1) list_tables.append(table_name) return list(set(list_tables)) createdTables = extract_table_info(logFilePath, crawlId, database) # initialize the Glue client and make batch_delete_table API calls to delete the created tables client = boto3.client('glue') for i in range(len(createdTables) // 100 + 1): tables_to_delete = createdTables[100 * i : 100 * (i + 1)] response = client.batch_delete_table( CatalogId=catalogId, DatabaseName=database, TablesToDelete=tables_to_delete ) print(i) print(response)
**注意:**请将示例输入参数替换为您的输入参数。前面的示例脚本使用本地计算机或 EC2 实例上的默认 AWS 区域设置。要在 boto3 中更改区域,请参阅配置。
