Buzeli
buzeliSoluções Digitais
成本

6周内AWS费用从$1,670降至$1,031:正确的索引→缩容→预留实例执行顺序

发布于 2026年5月4日

背景:AWS上的一个支付应用

一个运行在AWS us-east-1的支付平台。技术栈:EC2 Auto Scaling组使用c6g.2xlarge ARM实例,RDS MariaDB 10.11运行在db.r6g.2xlarge,ALB、WAF、EFS、ElastiCache。2026年2月账单:$1,670/月。

账单中有明显的优化空间——但操作顺序比操作本身更重要。本文记录了3个阶段、阶段之间的验证门控,以及跳过步骤时会发生什么。

核心教训:在确认正确的RDS尺寸之前购买预留实例,就是将资金锁定在错误的规格上。在我们的案例中,这意味着为一个3周后被缩容到xlarge的db.r6g.2xlarge支付$2,316/年的RI费用。

第一阶段(第1-2周):索引——CPU从94%降至26%,零成本

orders(订单)表是应用中查询最频繁的表——经销商报表、物流追踪定时任务、数据导出——但没有任何支持索引。一位开发者留下的索引创建脚本覆盖了13个次要表,却完全遗漏了系统中最关键的表。

诊断来自代码中的查询模式:每次报表请求、每次定时任务循环(每15分钟)、每次数据导出都对销售表进行全表扫描。RDS CPU峰值达到94.1%——这是一台有8个vCPU的db.r6g.2xlarge。这不是容量压力,而是无索引查询的压力。

复制
-- sales表中缺失的关键索引:
CREATE INDEX idx_tx_reseller_status
  ON orders (reseller, order_status) ALGORITHM=INPLACE LOCK=NONE;

CREATE INDEX idx_tx_vendor_status
  ON orders (vendor, order_status) ALGORITHM=INPLACE LOCK=NONE;

-- 物流追踪定时任务:每15分钟全表扫描
CREATE INDEX idx_tx_tracking
  ON orders (tracking_flag, tracking_last_updated) ALGORITHM=INPLACE LOCK=NONE;

-- 数据导出和报表
CREATE INDEX idx_sales_last_event_date
  ON orders (last_event_date) ALGORITHM=INPLACE LOCK=NONE;

CREATE INDEX idx_sales_created_at
  ON orders (created_at) ALGORITHM=INPLACE LOCK=NONE;

通过ALGORITHM=INPLACE LOCK=NONE在生产环境中3.91秒内创建了23个索引——无停机、不阻塞查询。执行前后的基准测试:

15个主要查询的总时间:624ms → 153ms(减少75%)

消除了13个全表扫描查询中的12个

RDS CPU峰值(下周):94.1% → 26.0%

这个阶段的成本:零。没有基础设施变更,没有预留实例,没有缩容。只是在正确的查询上建立复合索引。

复合索引(reseller, order_status)比等效的全表扫描快43倍用于报表查询。但这个收益只在数据适合InnoDB缓冲池的情况下存在——在小表中。随着表的增长,收益会增加。

门控1:在缩容之前等待完整的一个工作周

创建索引后,指示很明确:什么都不做,等一周。在做任何缩容决策之前,在完整的工作周(周一到周五)内监控RDS在新索引下的实际行为。

为什么要等整整一周?因为应用的负载模式是不规则的——来自社交媒体活动(TikTok、Instagram)的突发流量峰值。单个安静的周二不代表真实负载。整整一周的数据显示了绝对峰值和峰值的真实分布。

复制
# 收集CloudWatch指标(索引创建后一周:3月10-14日)
aws cloudwatch get-metric-statistics   --namespace AWS/RDS   --metric-name CPUUtilization   --dimensions Name=DBInstanceIdentifier,Value=myapp-db   --start-time 2026-03-10T00:00:00Z   --end-time 2026-03-15T00:00:00Z   --period 86400   --statistics Average Maximum

# 索引创建后一周的结果:
# 平均CPU(工作日):2.27%
# 峰值平均值:       13.6%
# 绝对峰值:         26.0%(之前是94.1%)

只有在确认这些数字——有5个工作日的数据——之后,才做出了缩容决策。

第二阶段(第3周):RDS缩容——db.r6g.2xlarge → xlarge

db.r6g.2xlarge(8 vCPU)上绝对峰值为26%,对db.r6g.xlarge(4 vCPU)的预测很清晰:百分比会翻倍,预计最大峰值约为~52%。可以接受。内存:64 GB中使用了6.6 GB——在32 GB的xlarge上,将剩余约25 GB空闲。

复制
# 为维护窗口安排缩容
aws rds modify-db-instance   --db-instance-identifier myapp-db   --db-instance-class db.r6g.xlarge   --no-apply-immediately

# 检查待处理修改
aws rds describe-db-instances   --db-instance-identifier myapp-db   --query 'DBInstances[0].PendingModifiedValues'
# 返回:{ "DBInstanceClass": "db.r6g.xlarge" }

# 计划维护窗口:3月15日 01:00-02:00 UTC
# 预估停机时间:10-15分钟

缩容在维护窗口期间自动应用。立即节省:$373/月(从On-Demand的$747降至$374)。

为什么不缩容到db.r6g.large(2 vCPU)? 预测显示峰值约为~82%——没有为新的无索引查询留余量,没有为突发流量留余量。额外节省的$213/月不足以抵消生产事故的风险。

门控2:在xlarge上等待一周后再购买RI

缩容后,再次:等一周什么都不做。原因简单且有其名字:在确认容量规格正确之前,不要预留容量。

一年期预留实例是无论使用与否每月$181的承诺。如果峰值高于预测,或者如果新功能带来了无索引查询导致峰值超过80%,下一步将是另一次缩容——而RI将被锁定在错误的规格中。

xlarge第一个工作周的数据(3月17-21日):

平均CPU:4.24%(预测约为~4.5%)

绝对峰值:40.9%(预测约为~52%)

内存:32 GB中30 GB空闲

事故:零

确认这些数字后,安全地做出了购买RI的决策。

第三阶段(第4周):预留实例——RDS + EC2

确认RDS规格后,预留实例模拟:

复制
# RDS RI模拟(通过AWS CLI)
aws rds describe-reserved-db-instances-offerings   --db-instance-class db.r6g.xlarge   --product-description "mariadb"   --multi-az false   --region us-east-1   --query 'ReservedDBInstancesOfferings[?OfferingType==`No Upfront`].[Duration,FixedPrice,RecurringCharges]'

# 1年No Upfront RI结果:
# $0/预付 + $0.248/小时 = $181/月
# vs On-Demand:$0.519/小时 = $374/月
# 节省:$193/月($2,316/年)

对于EC2,策略与显而易见的选择不同。ASG运行c6g.2xlarge实例——直接选择是预留1x c6g.2xlarge。选择的是带Scope=Region的2x c6g.xlarge。

AWS标准化单位技巧

Scope=Region的EC2预留实例使用标准化单位系统。每种实例规格都有一个标准化权重:

xlarge = 8个标准化单位

2xlarge = 16个标准化单位

2x c6g.xlarge = 16个标准化单位 = 完全等同于1x c6g.2xlarge。通过Scope=Region,这2个RI自动覆盖ASG中的c6g.2xlarge实例——无需手动关联,无需替换实例。

为什么预留xlarge而不是2xlarge? 未来的灵活性。如果ASG将来缩容到c6g.xlarge,2个RI覆盖2个完整实例——更好的覆盖率。预留2xlarge会将预留锁定在单一规格上,没有适应空间。

复制
# 最终购买的RI(2026-03-22):
# RDS:db.r6g.xlarge,1年No Upfront
#   $0/预付,$0.248/小时,$181/月 — 节省$193/月
#
# EC2:2x c6g.xlarge,Scope=Region,1年No Upfront
#   $0/预付,$0.0857/小时 x2,$125/月 — 节省$73/月
#   (通过16个标准化单位覆盖1x c6g.2xlarge)

结果:6周内从$1,670降至$1,031/月

整合三个阶段:

第一阶段 — 索引(第1-2周): CPU 94.1% → 26.0%。成本:$0。

第二阶段 — RDS缩容(第3周): On-Demand $747 → $374/月。节省:$373/月。

第三阶段 — 预留实例(第4周): RDS $374 → $181,EC2 $199 → $125。额外节省:$266/月。

总计: 节省$639/月。$7,668/年。

复制
# 账单对比(2月 vs 优化后):
# 资源                | 2月/2026  | 优化后     | 降幅
# -------------------|-----------|-----------|------
# RDS(计算)         | $747      | $181 (RI) | 76%
# EC2计算(1实例)    | $199      | $125 (RI) | 37%
# 其他                | ~$724     | ~$725     | ~0%
# 总计                | ~$1,670   | ~$1,031   | 38%
成本下降了38%。RDS是最大贡献者:结合缩容+RI降低了76%。EC2降幅较小,因为ASG仍然对第一个实例以外的额外实例使用On-Demand——RI只覆盖全天候运行的基础实例。

如果我们先购买RI会发生什么

这个假设场景很重要。如果预留实例在2月就已购买,在其他阶段之前:

db.r6g.2xlarge 1年No Upfront RI: $0.454/小时 = $330/月 — 相比On-Demand节省$417/月。

看起来不错——直到意识到3周后RDS将被缩容到xlarge。此时,2xlarge RI继续按$330/月收费,而xlarge实例产生单独的On-Demand费用(因为通过标准化单位覆盖的方式不同)。

实际结果:在能做出正确决策之前,要为错误规格的RI多付两个半月的费用——或者因为害怕'浪费'RI而被困在2xlarge上。在两种情况下,财务结果都比遵循正确顺序要差。

实用规则:只有在确认目标规格有2周稳定指标后,才购买预留实例。不能提前。

将顺序作为方法论

三个阶段的顺序不是偶然的。每个阶段都为下一个阶段创造条件:

索引优先: 揭示真实需要的容量。没有索引,服务器看起来需要比实际更多的CPU。

缩容其次: 了解真实负载后,安全地缩减实例。

RI最后: 确认规格后,安全地锁定1年的折扣。

颠倒这个顺序中的任何一对都会产生以下三个问题之一:

RI先于缩容: RI被锁定在错误的规格上。

缩容先于索引: 较小的实例在低效查询负载下 → 生产事故。

RI先于索引: 对一个将被缩容的超配实例给予折扣。

基础设施优化不是一个操作列表——它是一个带有验证门控的序列。门控是确认下一步操作安全的数据。没有门控,每个操作都是风险;有了门控,每个操作都是前一个操作的逻辑结果。

我们创建的索引及其生成的CPU指标在一篇关于RDS技术诊断的独立文章中有详细记录。本文专注于策略和顺序——'如何在您的公司做到这一点'的操作手册——而不是每个索引的技术细节。