企业文化

管理 Amazon S3 中的重复对象 存储博客

亚马逊 S3 中重复对象的管理

作者:Tamelly Lim 发表日期:2024 年 1 月 11 日 Amazon Athena,Amazon 简单存储服务 (S3),AWS Lambda,中级 (200),存储,技术指南 永久链接 评论 分享

重点摘要

在存储系统中管理大量数据时,数据重复的问题很常见。本文将讨论如何利用 Amazon S3、Amazon Athena 和 AWS Lambda 管理 S3 中的重复对象,帮助用户降低存储成本。通过识别、验证和删除重复数据,用户能够优化存储使用而不必手动删除文件。

识别重复对象

我们将使用对象的 实体标签ETag来整理重复对象的列表。ETag 是对象的哈希值,仅反映对象内容的变化,而非其元数据。对于使用 SSES3 加密或未加密并且未通过 分段上传或部分复制 操作上传的对象,ETag 是对象数据的 MD5 摘要。通过比较这些对象的 MD5 摘要,我们可以识别出即使在具有不同键或位于不同前缀的情况下也存在重复的对象。

管理 Amazon S3 中的重复对象 存储博客

此解决方案使用 Amazon S3 清单生成目标 S3 桶中所有对象及其元数据的列表,然后通过 Amazon Athena 查询该列表以识别重复对象。在审核 Athena 提供的结果并确保列出的对象可以安全删除后,我们使用 S3 批量操作调用 AWS Lambda 函数,执行大规模批量删除。这种方法帮助您在不需要手动挑选待删除对象的情况下,减少重复内容对象的存储成本。

海外加速器免费版

图 1:解决方案架构图,通过 Amazon Athena 查询 S3 清单报告识别要删除的重复对象,利用 S3 批量操作调用 Lambda 函数

解决方案概述

在您的 S3 桶中使用 Athena 识别重复对象,并查询 S3 清单 报告。然后,在您确认这些对象适合删除之后,通过 S3 批量操作删除这些对象,借助一个 Lambda 函数。

前提条件

继续本帖子所需的前提条件包括:

未启用版本控制的目标 S3 桶。适当的 AWS 身份与访问管理 (IAM) 权限,以便生成 S3 清单报告、创建 Athena 中的表和视图,以及配置 S3 批量操作作业。以下是具有最低权限要求的示例 IAM 策略,此外还有 AWS 管理的策略 AWSLambdaFullAccess。

json{ Version 20121017 Statement [ { Sid S3Actions Effect Allow Action [ s3GetObject s3DeleteObject s3PutObject s3PutBucketPolicy s3GetBucketPolicy s3ListBucket s3GetBucketLocation s3GetInventoryConfiguration s3PutInventoryConfiguration ] Resource [ arnawss3lttargetbucketgt arnawss3lttargetbucketgt/ ] } { Sid CreateS3BatchOperationsJob Effect Allow Action [ s3CreateJob s3ListAllMyBuckets s3ListJobs s3DescribeJob s3UpdateJobStatus ] Resource } { Sid Lambda Effect Allow Action [ iamCreateRole iamCreatePolicy iamAttachRolePolicy iamListPolicies iamCreatePolicyVersion ] Resource [ arnawsiam111122223333policy/servicerole/AWSLambdaBasicExecutionRole arnawsiam111122223333role/servicerole/DeleteS3Objects ] } { Sid S3BatchOperations Effect Allow Action iamPassRole Resource arnawsiam111122223333role/ltiambatchopsrolegt } { Sid RunAthenaQueries Effect Allow Action [ athenaGetWorkGroup athenaStartQueryExecution athenaStopQueryExecution athenaGetQueryExecution athenaGetQueryResults glueGetTables glueGetTable glueCreateTable glueGetDatabase glueGetDatabases ] Resource } ]}

操作步骤

在这个架构中,我们按照以下步骤配置所需的资源:

配置 S3 清单报告。使用 Athena 查询 S3 清单报告。创建删除单个对象的 Lambda 函数。配置 S3 批量操作作业以删除对象。

步骤 1:配置 S3 清单报告

S3 清单 允许您生成对象及其相关元数据的列表,以便查询和管理您的对象。我们利用此功能生成对象列表,以便您比较它们的 ETag 值。

在 Amazon S3 管理控制台 中选择存储重复对象的目标桶,导航至 管理 标签。在 清单配置 部分,选择 创建清单配置。设置配置名称和清单范围,通过限制到所需的前缀级别。对于对象版本,保留默认的 仅当前版本。如果您希望跨桶中的所有对象版本删除重复项,版本控制桶可以选择 包括所有版本。设置 目标桶 并将提供的示例策略应用于目标桶策略。您可以使用与目标桶不同的桶,便于管理。选择 频率、格式,并 启用 清单报告。频率可以选择 每日 或 每周。输出格式可以选择 CSV、Apache ORC 和 Apache Parquet。使用列式格式可以获得更好的查询性能,尤其是当您的目标 S3 桶中有数百万个对象时。本文使用 Apache ORC 作为输出格式。

在 附加元数据字段 下,选择 大小、上次修改、加密 以及 ETag 这几项。

选择 创建 完成配置。第一份报告将在接下来的 48 小时内到达您的目标桶。

步骤 2:使用 Athena 查询 S3 清单报告

Athena 是一种交互式查询服务,可以使用标准 SQL 轻松分析 Amazon S3 中的数据。它是无服务器的,无需管理基础设施,您只需为运行的查询付费。您可以使用它查询生成的 S3 清单报告,以识别可以删除的重复文件。默认情况下,Athena 不支持直接查询存储在 S3 Glacier Flexible Retrieval 或 S3 Glacier Deep Archive 存储类中的对象,因此归档对象将被忽略,无法查询。要查询归档对象,您必须首先恢复这些对象,然后才能对恢复的数据运行查询。有关如何使用 Amazon Athena 查询您的桶中的归档数据的更多指导,请参见这篇 博客文章。

在控制台中打开 Amazon Athena,并在左侧边栏中选择 查询编辑器选项卡。确保您在与 S3 桶相同的 AWS 区域。在 编辑器 标签中运行以下第一个查询。这将创建 Athena 中 S3 清单报告的表架构,并在运行查询时使用。如果您的报告是 Parquet 或 CSV 格式,则请参考 AWS 文档 来了解如何相应地创建表架构。对于启用版本控制的桶,需包含额外的版本 ID 列。

将 ltS3 Inventory Locationgt 替换为在步骤 1 中配置的 S3 清单位置。例如,如果我的桶名为 “s3duplicatebucket”,清单配置名为 “inventorycheck”,则用于 2023 年 12 月 12 日生成的报告的位置为 “s3//s3duplicatebucket/inventorycheck/hive/dt=202312130100/”。

如果桶中存在使用 SSEKMS 或 SSEC 加密的对象,请通过创建另一个视图筛选出这些对象,仅选择其加密状态为 SSES3 或 NOTSSE 的对象。然后在后续步骤中从创建的视图查询。

sqlCREATE EXTERNAL TABLE s3inventory( bucket string key string size bigint lastmodifieddate timestamp etag string encryptionstatus string) ROW FORMAT SERDE orgapachehadoophiveqlioorcOrcSerde STORED AS INPUTFORMAT orgapachehadoophiveqlioSymlinkTextInputFormat OUTPUTFORMAT orgapachehadoophiveqlioIgnoreKeyTextOutputFormat LOCATION ltS3 Inventory Locationgt

创建表架构后,接下来的查询帮助创建一个视图,该视图列出 S3 清单报告中多个重复的 ETag。运行以下查询。

sqlCREATE VIEW duplicateetag AS SELECT DISTINCT etag FROM defaults3inventory GROUP BY etag HAVING COUNT(key) gt 1

为了确定保留哪个重复对象,我们保留具有最新 最后修改日期 的对象。这样,我们通过比较每个对象的 ETag 和对象大小后,确定哪些是重复对象。此查询识别每个独特 ETag 的最新 最后修改日期。

sqlCREATE VIEW etagmaxdate as SELECT etag size MAX(lastmodifieddate) as maxdate from defaults3inventoryGROUP BY etag size

最后一个查询生成一个要删除的对象列表,排除了 etagmaxdate 中的对象,这些对象是在桶中保留的对象。对于启用版本控制的桶,需要在筛选查询中包括版本 ID。

sqlSELECT s3inventorybucket s3inventorykeyFROM (s3inventoryLEFT JOIN etagmaxdate ON ((s3inventoryetag = etagmaxdateetag) AND (s3inventorylastmodifieddate = etagmaxdatemaxdate) AND (s3inventorysize = etagmaxdatesize)))WHERE (etagmaxdatemaxdate IS NULL)

下载结果,审核列出的键,确保它们可以安全删除。审核列出的对象非常重要,以确保它们适合删除,因为一旦它们在后续步骤中被永久删除,就无法恢复。请移除标题行,并将其保存为 CSV,以便稍后用于 S3 批量操作。

步骤 3:创建用于删除单个对象的 Lambda 函数

Lambda 是一种无服务器事件驱动的计算服务,允许您运行代码而无需提供或管理服务器。为了删除每个对象,我们配置一个 Lambda 代码,调用删除 API。

前往 Lambda 控制台 页面,确保处于相同的 AWS 区域。选择侧边导航栏上的 Dashboard,选择 创建函数。保持模式为 从头开始创建,函数名称输入 DeleteS3Objects,选择最新版本的 Python 作为运行时,其他设置保持默认,点击 创建函数。在函数页面,将以下代码插入到代码源中。该代码删除任何传递给函数的键。对于启用版本控制的桶,您必须通过删除对象 API 调用中的传递版本 ID 来永久删除该对象版本。

pythonimport loggingfrom urllib import parseimport boto3from botocoreexceptions import ClientError

logger = logginggetLogger(name)loggersetLevel(INFO)

s3 = boto3client(s3)

def lambdahandler(event context) Permanently deletes the object SPECIFIED OBJECT VERSION WILL BE PERMANENTLY REMOVED

param event The S3 batch event that contains the key of the object              to removeparam context Context about the eventreturn A result structure that Amazon S3 uses to interpret the result of the         operation When the result code is TemporaryFailure S3 retries the         operation# Parse job parameters from Amazon S3 batch operationsloggerinfo(event)invocationid = event[invocationId]invocationschemaversion = event[invocationSchemaVersion]results = []resultcode = Noneresultstring = Nonetask = event[tasks][0]taskid = task[taskId]try    objkey = parseunquoteplus(task[s3Key] encoding=utf8)    bucketname = task[s3BucketArn]split()[1]    loggerinfo(Got task remove object s objkey)    try        s3deleteobject(            Bucket=bucketname Key=objkey        )        resultcode = Succeeded        resultstring = (            fSuccessfully removed object {objkey}        )        loggerinfo(resultstring)                    loggerwarning(resultstring)