优化 AWS S3 存储成本指南





0/5 (0投票)
本文旨在优化 S3 存储成本。
目录
AWS 对 S3 如何收费?
在了解如何节省 S3 成本之前,我们需要了解 S3 如何向我们收费。S3 定价页面 (https://aws.amazon.com/s3/pricing/) 显示了其定价模型的详细信息。截至本文撰写时,S3 不仅收取存储费,还收取数据检索、数据传输、安全、监控和分析、复制以及转换等费用。尽管 S3 宣传按使用量付费,但从根本上说,某些功能的成本是不可避免的。
上图显示了我们使用 S3 时将执行的最低限度操作——摄取、存储和读取数据。因此,S3 的总成本可以简化为:
Total Cost = Storage Cost + Request Cost + Transfer Cost + Other Cost
在所有 S3 功能中,存储大小是方程中的关键因素,它会影响其他操作的收费金额。话虽如此,本文不讨论传输、复制和加密等成本,因为大多数情况下,我们使用这些功能是因为我们需要它们,而且它们是按需的。因此,我们无法从这些功能中节省太多费用。
将数据存储在合适的存储类别中
存储在 S3 中的数据并非总是以相同的方式处理。相反,S3 中的每个对象都关联一个存储类别——不同的存储类别提供不同的数据访问性能和功能,价格也不同。可用存储类别的完整列表可在 Amazon S3 存储类别 中找到。默认情况下,数据存储在 标准类别 中——这是最高效也是最昂贵的类别。因此,将数据保存在适当的类别中可以为我们节省大量资金。例如,下表显示了 100TB 数据在不同存储类别中的成本,以及与标准类别相比可以节省多少费用。
类 | 每月费率(截至 2024 年 2 月) | 总计 | 保存 |
标准版 | 前 50TB:$0.023/GB 后 450TB:$0.022/GB | 50 * 1024 GB * 0.023 + 50 * 1024 GB * 0.022 = $2,304 | 不适用 |
标准-IA | $0.0125/GB | 100 * 1024 GB * 0.0125 = $1,280 | $1,024 |
Glacier 瞬时检索 | $0.004/GB | 100 * 1024 GB * 0.004 = $409.6 | $1,894.4 |
Glacier 灵活检索 | $0.0036/GB | 100 * 1024 GB * 0.0036 = $368.6 | $1,935.4 |
Glacier 深度归档 | $0.00099/GB | 100 * 1024 GB * 0.00099 = $101.38 | $2,202.62 |
尽管示例展示了通过将数据存储在其他类别中可以节省多少费用,但细节决定成败——存储成本更低的类别具有更高的检索成本。此外,如果数据存储在深度归档类别中,数据需要在访问前恢复。下面的示例计算了从不同类别检索 100TB 数据并执行 1M GET 请求的成本。
类 | 每 1000 次请求的 GET 请求费率(截至 2024 年 2 月) | GET 请求 | 每 GB 数据检索费率(截至 2024 年 2 月) | 检索 | 总计 |
标准版 | $0.0004 | 1,000,000 / 1,000 * 0.0004 = $0.4 | 不适用 | $0 | $0.4 |
标准-IA | $0.001 | 1,000,000 / 1,000 * 0.001 = $1 | $0.01 | 100 * 1024 GB * 0.01 = $1024 | $1025 |
Glacier 瞬时检索 | $0.01 | 1,000,000 / 1,000 * 0.01 = $10 | $0.03 | 100 * 1024 GB * 0.03 = 3072 | $3082 |
Glacier 灵活检索 | $0.0004 | 1,000,000 / 1,000 * 0.0004 = $0.4 | $0.01 (标准) | 100 * 1024 GB * 0.01 = $1024 | $1024.4 |
Glacier 深度归档 | $0.0004 | 1,000,000 / 1,000 * 0.0004 = $0.4 | $0.02 (标准) | 100 * 1024 GB * 0.02 = $2048 | $2048.4 |
这个例子表明,如果我们将数据存储在最便宜的类别中却忽略了其他因素,我们可能会花费更多的钱。
选择适当的存储类别
上一节展示了如果我们将数据存储在合适的类别中可以节省多少钱,以及如果选择了错误的类别可能会花费多少钱。那么,我们如何选择一个适用的类别呢?答案是这取决于具体情况。选择存储类别基于访问模式——我们已知的模式和我们未知的模式。
具有已知或可预测访问模式的数据
如果我们知道需要访问所存储数据的频率,我们可以将它们存储在最具成本效益的类别中。例如,银行被要求在客户关闭账户后将客户账户记录保留五年,而在这五年内检索数据的可能性非常小。因此,将数据存储在不常访问甚至归档类别中是有意义的,只要从非标准类别中节省的存储费用超过数据检索的成本。我们可以使用以下等式来评估我们是否应该将数据移动到非标准类别,如果是,应该选择哪一个。
,其中
是要存储的数据总大小。
是在标准类别中存储数据的成本。
是在不常访问或归档类别中存储数据的成本。
是将发出的请求总数。
是在不常访问或归档类别中每个请求的成本。
是在标准类别中每个请求的成本。
是要检索的数据总大小。
是从不常访问或归档类别中检索数据的成本。
是一个阈值,定义了节省的金额超过一定数值,这样我们就可以放心地将数据存储在非标准类别中,以防万一。
在银行示例中,假设银行有 100TB 已关闭客户账户数据,必须保留五年。他们的经验告诉我们访问数据的可能性非常低,因此银行估计最大检索数据量不能超过 100TB,请求次数为 1M(换句话说,在五年内所有数据都访问一次)。因此,我们可以通过应用上述等式来估算成本。
非标准类别 | 与标准类别相比的存储节省 | 来自非标准类别的额外 GET 请求成本 | 来自非标准类别的额外检索成本 | 与标准类别相比的总节省 |
标准-IA | $1,024 * 60 = $61,440 | 1,000,000 / 1,000 * (0.001 – 0.0004) = $0.6 | $1,024 | $61,440 – $0.6 – $1024 = $60,415.4 |
Glacier 瞬时检索 | $1,894.4 * 60 = $113,664 | 1,000,000 / 1,000 * (0.01 – 0.0004) = $9.6 | $3,072 | $113,664 – $9.6 – $3072 = $110,582.4 |
Glacier 灵活检索 | $1,935.4 * 60 = $116,124 | 1,000,000 / 1,000 * (0.0004 – 0.0004) = $0 | $1,024 | $116,124 – $0 – $1024 = $115,100 |
Glacier 深度归档 | $2,202.62 * 60 = $132,157.2 | 1,000,000 / 1,000 * (0.0004 – 0.0004) = $0 | $2,048 | $132,157.2 – $0 – $2048 = $130,109.2 |
(存储节省和检索成本的数字来自上一节的示例)
该表展示了通过应用该等式我们可以节省多少钱。当然,我们仍然需要考虑将数据存储在非标准类别中的其他因素,例如从 Glacier 深度归档检索数据所需的时间。然而,至少它为我们提供了潜在存储成本和节省的粗略概念。
在类别之间移动数据
默认情况下,S3 会将新创建的对象存储在标准类别中,除非我们在存储数据时指定存储类别,例如使用 Boto3 put_object API,它允许将对象放入精确的存储类别中。然而,在大多数情况下,我们将数据从标准类别移动到非标准类别,在类别之间传输数据最有效的方式是利用 S3 生命周期。通过生命周期,我们可以定义规则来对一组对象执行特定操作。例如,在上一节提到的银行示例中,我们可以配置一个生命周期策略,在客户账户关闭后将客户记录从标准类别移动到 Glacier 深度归档,并在五年后删除数据。
尽管 S3 生命周期是在类别之间移动数据最有效的方式,但它并非免费,因此生命周期成本需要包含在 S3 总成本方程中。
总成本 = 存储成本 + 请求成本 + 传输成本 + 生命周期成本
具有未知访问模式的数据
当访问模式未知或不可预测时,AWS S3 有一个解决方案——智能分层。
智能分层
智能分层通过监控数据的访问方式,根据访问模式自动将数据移动到最具成本效益的层级。智能分层类别中的层级与其他存储类别不同。请参阅下图。
与其他存储类别不同,智能分层没有数据检索费,并且所有层级的请求成本都相同。但是,监控和自动化对象会产生需要考虑的成本。
Total Cost = Storage Cost + Monitoring Fee
在这个等式中,存储成本是每个层级存储成本的总和,监控费用是根据对象数量计算的。
智能分层类别是优化 S3 存储成本的绝佳方式。AWS S3 建议在大多数情况下使用智能分层。但是,仍有改进成本的空间。
首先,对象大小很重要。小于 128KB 的对象将不会被监控,因此它们不会被移动到不同的层级,并且将按照频繁访问层级的费率收费(请参阅 工作原理 部分的自动访问层级)。因此,应避免存储小于 128KB 的对象。
其次,对象数量很重要。截至本文撰写时,智能分层中的监控和自动化成本为每 1000 个对象 0.0025 美元,这意味着在数据量相同的情况下,如果一个有更多大对象,而另一个有更多小对象,后者需要比前者支付更多的监控费用。下表展示了两种情况之间的差异——一种的平均对象大小为 1 MB,另一种为 100 MB,但两者的数据总大小均为 100 TB。
对象大小 | 对象数量 | 监控成本 |
1 MB | 104,857,600 | 104,857,600 / 1000 * 0.0025 = $262.14 |
100 MB | 1,048,576 | 1,048,576 / 1000 * 0.0025 = $2.62 |
该示例清楚地表明,在小对象的情况下,监控和自动化费用远高于大对象的情况。当然,不太可能每个对象都是相同的大小,但这让我们了解到对象数量的重要性。
第三,如果数据访问模式稳定,我们可能无法从智能分层中获得好处,反而会支付不必要的额外费用。例如,如果所有数据都被积极访问,则没有数据在层级之间移动,所有数据都存储在频繁访问层级。但智能分层类别仍会收取监控费用。因此,我们支付了额外的监控费用却未获得任何好处。
监控和分析
如果我们不衡量 S3 使用情况并监控其行为,我们就不了解自己的表现如何。AWS 提供了几种获取 S3 洞察的选项——存储透镜、存储类别分析 和 S3 清单。
- 存储透镜 提供账户级别或组织范围的存储使用情况和活动趋势洞察、详细指标以及成本优化建议。
- 存储类别分析 监控数据访问模式,并根据对象的创建时间将数据分类为频繁访问或不频繁访问。分析报告包括对象创建时间、对象数量和大小、请求数量以及上传、存储和检索的数据大小等指标。
- S3 清单 报告对象级别的指标,例如版本 ID、大小、存储类别和智能分层类别的层级。
通过这些监控和分析功能生成的报告,我们可以评估我们的存储使用情况,以确保我们使用 S3 的方式得到优化。然而,除了存储透镜的默认仪表板外,所有监控和分析功能都有其成本。此外,存储类别分析和 S3 清单可以将报告导出并存储在 S3 中。这些文件需要支付 S3 存储费用。根据导出频率和监控对象数量,存储类别分析和 S3 清单生成的文件可能会增长非常快并消耗大量存储空间,这必须在 S3 成本优化计划中加以考虑。
对象数量很重要
与智能分层类别中的监控和自动化费用类似,所有监控和分析都按监控的对象数量收费。此外,对象数量越少,所需的 PUT
、GET
和所有其他请求就越少。生命周期转换请求也会更便宜。因此,我们应该在数据量相同的情况下,尽可能地使对象更紧凑。这不仅可以提高数据访问性能,还可以节省按对象数量收费的 S3 功能的费用。
最佳实践
本节介绍了一些可能在类似情况下有用的用例。
将智能分层设为默认
由于智能分层类别在许多用例中是首选类别,因此将其作为默认存储类别是有意义的。然而,新创建的对象默认存储在标准类别中。幸运的是,有两种方法可以将新创建的对象立即放入智能分层类别,从而使智能分层类别作为默认存储类别。
第一种方法是在存储数据时指定类别。如果我们控制将数据放入 S3 的生产者,我们可以在调用 PUT API 或 SDK(例如 Boto3 put_object)时指定智能分层作为存储类别。
然而,在大多数情况下,我们无法控制生产者,因此第二种方法是利用 S3 生命周期,在对象一放入 S3 后立即将其移动到智能分层类别。下面的生命周期示例展示了这一点。
生命周期立即将新创建的对象移动到智能分层(请注意,上述生命周期策略还在七天后删除非当前版本)。
ETL
通常,ETL 的访问模式是可预测和稳定的,典型的 ETL 有三个步骤——提取、转换和加载,如下图所示。
在提取步骤中,ETL 从源提取数据;原始数据被存储(例如,此示例中的 s3://my_bucket/raw/
)。转换步骤读取原始数据并对其进行处理(假设它只读取尚未处理的数据)。在转换过程中,可能会创建和存储一些临时数据(例如,s3://my_bucket/temp/
)。数据处理完成后,ETL 将处理后的数据加载到目标位置(例如,s3://my_bucket/table/
),用户可以从中查询。
假设管道每天运行一次,在此设置中,原始数据将被写入一次并读取一次。因此,对于 s3://my_bucket/raw/
中的数据,像下面这样的生命周期策略将是一个不错的选择。
原始数据将在标准类别中保留三十天,以防我们需要调试任何问题。之后,对象将被移动到 Glacier 瞬时检索类别,并在 90 天后过期。如果对象变为非当前,它将在 30 天后被删除。
关于处理中的数据(存储在 s3://my_bucket/temp/
中),由于它是临时数据,并且 ETL 每天运行一次,我们可以设置一个生命周期策略来删除临时数据,如下例所示。
最后,数据处理完成后,将存储在 s3://my_bucket/table/
,并由应用程序和用户频繁访问,因此默认的标准类别是最佳选择——无需将数据移动到其他类别。
正确删除数据
删除数据时,我们需要确保数据已被删除,尤其是在以下场景中。
版本控制已启用
当启用版本控制时,无论是通过 S3 控制台、API、SDK 还是 CLI 发出的 DELETE
操作,都不会永久删除对象。相反,S3 会在存储桶中插入一个删除标记,并且该删除标记成为具有新对象 ID 的当前对象版本。
当我们尝试 GET
已删除对象(即,对象的当前版本是删除标记)时,S3 返回 Not Found
错误——它的行为就像对象已被删除一样。但是,如果我们在 S3 控制台中启用 显示版本
,我们可以看到删除标记,并且所有非当前版本仍然存在。
因此,该对象仍然存在于存储桶中,我们继续支付其存储费用。有几种方法可以确保对象确实被删除(当然,在我们确实想删除它们的情况下)。在使用 S3 控制台时,我们需要启用“显示版本”选项才能查看并选择要删除的对象。
值得一提的是,通过 S3 控制台删除对象时的确认消息。如果删除带有版本 ID 的对象,确认消息是 永久删除
(这与在禁用版本控制的存储桶中删除对象时相同)。相反,如果删除没有版本 ID 的对象,确认消息只是 删除
。因此,从消息中我们可以判断对象是否真的被删除了。
一种编程方式是使用生命周期策略清理已删除的对象。例如,我们可以使用如下所示的生命周期策略来确保对象被删除。
当一个对象被删除时,会创建一个删除标记并成为当前版本;原始对象变为非当前。此生命周期策略会在对象被删除(即变为非当前)30 天后永久删除它们,当删除标记没有非当前对象时,删除标记将变为过期对象删除标记,并同样会被生命周期策略删除。换句话说,在对象被删除 30 天后,该对象,包括版本及其删除标记,将被永久删除。
请注意,生命周期无法在不使对象过期的情况下永久删除对象。如果可以,则与启用版本控制的存储桶的目的相矛盾。因此,如果我们要始终永久删除对象(即不添加删除标记),我们应该使用禁用版本控制的存储桶。
此外,通过 API、SDK 或 CLI 删除对象时,我们必须指定对象的版本 ID,以确保对象被永久删除。在这种情况下,S3 不会创建删除标记,并将永久删除对象的特定版本。
AWS S3 有一份详细的文档描述了在启用版本控制的存储桶中删除是如何工作的:从启用版本控制的存储桶中删除对象版本 – Amazon 简单存储服务
查询引擎元数据
S3 是大数据解决方案的常见构建块——使用 S3 作为存储层,其上有一个查询引擎(例如 Databricks 和 Snowflake),以便人们可以像使用存储在 S3 中的数据库一样查询对象。查询引擎维护其元数据以管理存储在 S3 中的对象并更好地执行。根据查询引擎的设计和配置,删除行为可能与我们的预期不同。例如,在 Databricks 中的外部表 上使用 DROP TABLE
命令只删除元数据,而不删除数据;数据本身仍然存在于 S3 中,我们继续支付存储费用。因此,在使用带有 AWS S3 的查询引擎时,我们需要注意查询引擎如何与 S3 交互,尤其是在删除数据时。
避免冗余备份数据
AWS S3 提供了出色的选项来备份数据并保留数据的历史记录,例如 S3 复制和版本控制,我们预计备份数据会增加存储占用空间。然而,在使用带有 S3 的应用程序时,备份数据增加的大小可能会让我们大吃一惊。以下场景演示了可能出现的问题。
假设我们使用 Databricks 和 S3,Databricks 提供了一个名为 时间旅行 的功能,它允许我们回溯到 Delta 表 的旧版本。为了使时间旅行起作用,Databricks 需要保留表的每个版本的副本(在这种情况下存储在 S3 中)。考虑一个每小时运行一次并执行覆盖方法的 Spark 作业,如下面的代码所示。
# read the source
df = spark.read...
# some transformation
df = ...
# write to the target
df.write.format("delta").mode("overwrite").save("<S3 Location>")
如果作业写入的大小约为 1GB,由于时间旅行功能,一年后,S3 中将存储 8,760GB (365 * 24)。通常,我们在 Spark 中使用 overwrite
模式的原因是我们不关心被覆盖的旧数据。我们也可能认为通过覆盖不会产生不必要的备份,但我们可能不知道应用程序一直在进行备份。更糟糕的是,S3 生命周期在这种情况下无济于事。原因是写入 S3 的每个副本都被视为新对象(即当前版本),因此旧副本永远不会变为非当前。因此,我们无法使用生命周期策略删除旧副本。删除当前版本很困难,因为 S3 不知道应用程序端发生了什么;它可能会意外删除我们需要的数据。
在此 Databricks 用例中的解决方案是利用 VACUUM 命令,该命令旨在清理时间旅行功能创建的旧副本。
不幸的是,没有单一的解决方案来处理这种情况,这取决于应用程序。我们能做的就是了解应用程序如何与 S3 协作。
优化数据
在“对象数量很重要”一节中,我们了解到对象数量对 S3 成本影响很大。一个普遍的原则是使对象更大但数量更少。一些应用程序,例如查询引擎,可以使对象紧凑。例如,Databricks 提供了 OPTIMIZE 命令,将小对象合并为大对象。如果我们使用的应用程序具有这种能力,我们应该加以利用。
不要监控不必要的东西
我们都知道监控数据的重要性。然而,S3 中的监控并非免费,因此我们必须仔细选择要监控的内容。通常,我们监控其访问模式和使用情况未知的数据,以便我们可以相应地制定 S3 计划或通过审查监控报告调整我们的 S3 策略。相反,我们不会从监控我们已知的事物中获得太多价值——这是我们应该避免的。
我们通常不需要监控的数据类型
- 临时数据。临时数据通常存在时间短;我们没有理由监控它。
- 着陆数据。在典型的 ETL 中,着陆数据通常是原始数据,需要转换。这些数据通常只读取一次。
- 具有稳定和可预测访问模式的数据。
- 日志数据。日志数据通常很小,但许多不常访问,最终将被删除。
- 冷数据。冷数据主要用于备份,很少访问。
此外,我们应该密切关注监控费用,以确保监控费用不超过我们可能从监控中获得的节省。当有大量小对象需要监控时,这可能会发生,并且由于对象很小,我们将无法从将对象移动到更便宜的类别中获得太多节省。讽刺的是,这在不衡量的情况下很难知道。避免这种情况的唯一方法是定期审查我们的监控设置和监控报告。
监控我们的数据至关重要,但它是有代价的;请明智地使用它。
摘要
以下是一些可能有助于优化 S3 成本的技巧。
- 将数据存储在最合适的存储类别中。
- 除了以下情况,优先选择智能分层。
- 数据访问模式已知、可预测且稳定。
- 将被删除的临时数据(尤其是在短时间内)
- 不再活跃或需要的生命周期数据。
- 不再需要的生命周期旧版本。
- 将对象合并为更大但数量更少。
- 定期使用存储透镜、清单或分析审查 S3 洞察。即使所有内容都处于智能分层中,通过检查数据洞察,未知的访问模式也可能变得已知。
- 当启用版本控制时,确保有生命周期策略来清理已删除的对象。
- 了解应用程序如何与 S3 交互。
使用 S3 的成本受许多因素影响,不可能有一个解决方案能够持续自动优化我们的 S3 存储成本。确保 S3 成本得到优化的唯一方法是持续审查我们的 S3 使用情况并相应地调整我们的方法。