De $1.670 para $1.031/mês em AWS em 6 semanas: a sequência correta de índice → rightsizing → Reserved Instance
Publicado em 4 de maio de 2026
O contexto: uma aplicação de pagamentos na AWS
Uma plataforma de pagamentos rodando em AWS us-east-1. Stack: EC2 Auto Scaling Group com instâncias c6g.2xlarge ARM, RDS MariaDB 10.11 em db.r6g.2xlarge, ALB, WAF, EFS, ElastiCache. Faturamento de fevereiro de 2026: $1.670/mês.
A conta tinha espaço óbvio de otimização — mas a sequência das ações importava mais do que as ações em si. Este post documenta as 3 fases, os gates de validação entre elas, e o que acontece quando você pula etapas.
A lição central: comprar uma Reserved Instance antes de confirmar o tamanho certo do RDS é travar capital no tamanho errado. No nosso caso, isso teria significado $2.316/ano de RI para um db.r6g.2xlarge que foi reduzido para xlarge 3 semanas depois.
Fase 1 (semanas 1-2): índices — CPU de 94% para 26%, custo zero
A tabela transactions era a mais consultada da aplicação — relatórios de revendedores, cron de rastreamento, exportações de dados — mas não tinha nenhum índice de suporte. O script de criação de índices deixado por um desenvolvedor cobria 13 tabelas secundárias e omitia completamente a tabela mais crítica do sistema.
O diagnóstico veio dos padrões de query no código: full scans em transactions a cada requisição de relatório, a cada ciclo do cron (a cada 15 minutos), a cada exportação. A CPU do RDS tinha picos de 94.1% — em um db.r6g.2xlarge com 8 vCPUs. Não era pressão de capacidade, era pressão de queries sem índice.
-- Índices críticos ausentes na tabela transactions:
CREATE INDEX idx_tx_revendedor_status
ON orders (revendedor, status_pedido) ALGORITHM=INPLACE LOCK=NONE;
CREATE INDEX idx_tx_fornecedor_status
ON orders (fornecedor, status_pedido) ALGORITHM=INPLACE LOCK=NONE;
-- Cron de rastreamento: full scan a cada 15 minutos
CREATE INDEX idx_tx_rastreamento
ON orders (pago, codigo_rastreamento) ALGORITHM=INPLACE LOCK=NONE;
-- Exportações e relatórios
CREATE INDEX idx_tx_data_ultimo_evento
ON orders (data_ultimo_evento) ALGORITHM=INPLACE LOCK=NONE;
CREATE INDEX idx_tx_created_at
ON orders (created_at) ALGORITHM=INPLACE LOCK=NONE;23 índices criados em produção em 3.91 segundos via ALGORITHM=INPLACE LOCK=NONE — sem downtime, sem bloqueio de queries. Resultado do benchmark antes e depois:
Tempo total das 15 queries principais: 624ms → 153ms (redução de 75%)
12 de 13 queries que faziam full table scan eliminadas
Pico de CPU RDS (semana seguinte): 94.1% → 26.0%
Custo com essa fase: zero. Nenhuma mudança de infraestrutura, nenhuma Reserved Instance, nenhum resize. Apenas índices compostos nas queries certas.
O índice composto (revendedor, status_pedido) é 43x mais rápido que o full scan equivalente para as queries de relatório. Mas esse ganho só existe enquanto o dado está no InnoDB buffer pool — em tabelas pequenas. Conforme a tabela cresce, o ganho aumenta.
Gate 1: aguardar uma semana comercial completa antes de rightsize
Após a criação dos índices, a instrução era clara: não fazer nada por uma semana. Monitorar o comportamento real do RDS com os novos índices em produção durante uma semana comercial completa (segunda a sexta) antes de qualquer decisão de resize.
Por que uma semana inteira? Porque o padrão de carga da aplicação era irregular — picos de tráfego burst vindos de disparos em redes sociais (TikTok, Instagram). Uma única terça-feira tranquila não representava a carga real. Os dados de uma semana completa mostravam o pico absoluto e a distribuição real dos picos.
# Coleta de métricas CloudWatch (semana pós-índices: 10-14/mar)
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
# Resultado semana pós-índices:
# Média CPU (dias úteis): 2.27%
# Média dos picos: 13.6%
# Pico absoluto: 26.0% (era 94.1%)Apenas após confirmar esses números — com 5 dias úteis de dados — foi tomada a decisão de rightsize.
Fase 2 (semana 3): rightsizing do RDS — db.r6g.2xlarge → xlarge
Com o pico absoluto em 26% no db.r6g.2xlarge (8 vCPU), a projeção para o db.r6g.xlarge (4 vCPU) era clara: percentuais dobrariam, pico máximo projetado em ~52%. Aceitável. Memória: 6.6 GB em uso de 64 GB — no xlarge com 32 GB, sobraria ~25 GB.
# Agendar rightsizing para a janela de manutenção
aws rds modify-db-instance --db-instance-identifier myapp-db --db-instance-class db.r6g.xlarge --no-apply-immediately
# Verificar pending modification
aws rds describe-db-instances --db-instance-identifier myapp-db --query 'DBInstances[0].PendingModifiedValues'
# Retorna: { "DBInstanceClass": "db.r6g.xlarge" }
# Janela de manutenção agendada: 15/mar 01:00-02:00 UTC
# Downtime estimado: 10-15 minutosO rightsizing foi aplicado automaticamente na janela de manutenção. Economia imediata: $373/mês (de $747 para $374 On-Demand).
Por que não fazer rightsizing para db.r6g.large (2 vCPU)? A projeção mostrava picos de ~82% — sem margem para queries novas sem índice, sem margem para tráfego burst. A economia adicional de $213/mês não compensava o risco de incidente em produção.
Gate 2: aguardar uma semana com xlarge antes de comprar RI
Após o rightsizing, novamente: não fazer nada por uma semana. A razão é simples e tem nome: você não reserva capacidade antes de confirmar que aquela capacidade é a certa.
Uma Reserved Instance de 1 ano é um compromisso de $181/mês independente do uso. Se os picos tivessem sido maiores do que o projetado, ou se uma nova feature tivesse chegado com queries sem índice causando picos acima de 80%, o próximo passo seria outro rightsizing — e a RI estaria travada no tamanho errado.
Os dados da primeira semana comercial com o xlarge (17-21/mar):
Média CPU: 4.24% (projeção era ~4.5%)
Pico absoluto: 40.9% (projeção era ~52%)
Memória: 30 GB livres de 32 GB
Incidentes: zero
Com esses dados confirmados, a decisão de comprar RI foi tomada com segurança.
Fase 3 (semana 4): Reserved Instances — RDS + EC2
Com o tamanho do RDS confirmado, a simulação de Reserved Instances:
# Simulação RI RDS (via 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]'
# Resultado RI 1 ano No Upfront:
# $0/upfront + $0.248/h = $181/mes
# vs On-Demand: $0.519/h = $374/mes
# Economia: $193/mes ($2.316/ano)Para o EC2, a estratégia foi diferente do óbvio. O ASG rodava instâncias c6g.2xlarge — a opção direta seria reservar 1x c6g.2xlarge. A escolha foi 2x c6g.xlarge com Scope=Region.
O truque das Normalized Units da AWS
Reserved Instances EC2 com Scope=Region funcionam com um sistema de Normalized Units. Cada tamanho de instância tem um peso de normalização:
xlarge = 8 normalized units
2xlarge = 16 normalized units
2x c6g.xlarge = 16 normalized units = equivalente exato a 1x c6g.2xlarge. Com Scope=Region, esses 2 RIs cobrem automaticamente a instância c6g.2xlarge do ASG — sem necessidade de associação manual, sem substituição de instância.
Por que reservar xlarge em vez de 2xlarge? Flexibilidade futura. Se o ASG fizer rightsizing para c6g.xlarge no futuro, os 2 RIs cobrem 2 instâncias inteiras — melhor cobertura. Reservar 2xlarge travaria a reserva em um único tamanho sem possibilidade de adaptação.
# Resultado final das RIs compradas (2026-03-22):
# RDS: db.r6g.xlarge, 1a No Upfront
# $0/upfront, $0.248/h, $181/mes — economia $193/mes
#
# EC2: 2x c6g.xlarge, Scope=Region, 1a No Upfront
# $0/upfront, $0.0857/h x2, $125/mes — economia $73/mes
# (cobre 1x c6g.2xlarge via 16 normalized units)O resultado: $1.670 → $1.031/mês em 6 semanas
Consolidando as três fases:
Fase 1 — Índices (semanas 1-2): CPU 94.1% → 26.0%. Custo: $0.
Fase 2 — Rightsizing RDS (semana 3): $747 → $374/mês On-Demand. Economia: $373/mês.
Fase 3 — Reserved Instances (semana 4): RDS $374 → $181, EC2 $199 → $125. Economia adicional: $266/mês.
Total: $639/mês economizados. $7.668/ano.
# Billing comparativo (fev vs pós-otimização):
# Recurso | Fev/2026 | Pós-otimização | Redução
# ---------------------|-----------|----------------|--------
# RDS (compute) | $747 | $181 (RI) | 76%
# EC2 Compute (1 inst) | $199 | $125 (RI) | 37%
# Demais | ~$724 | ~$725 | ~0%
# Total | ~$1.670 | ~$1.031 | 38%O custo caiu 38%. O RDS foi o maior contribuidor: 76% de redução combinando rightsizing + RI. O EC2 teve redução menor porque o ASG ainda usa On-Demand para instâncias extras acima da primeira — a RI cobre apenas a instância base que roda 24/7.
O que teria acontecido se comprássemos a RI primeiro
Esse cenário hipotético é importante. Se a Reserved Instance tivesse sido comprada em fevereiro, antes das outras fases:
RI para db.r6g.2xlarge 1 ano No Upfront: $0.454/h = $330/mês — economia de $417/mês vs On-Demand.
Parece bom — até perceber que 3 semanas depois o RDS seria reduzido para xlarge. Nesse momento, a RI de 2xlarge continua gerando cobrança a $330/mês, enquanto a instância xlarge gera cobrança de On-Demand à parte (porque o tamanho não é coberto pela RI de 2xlarge via normalized units da mesma forma).
O resultado real: pagaria dois meses e meio de RI no tamanho errado antes de poder tomar a decisão certa — ou ficaria preso no 2xlarge por medo de 'desperdiçar' a RI. Nos dois cenários, o outcome financeiro é pior do que seguir a sequência correta.
A regra prática: só compre Reserved Instance após confirmar 2 semanas de métricas estáveis no tamanho alvo. Não antes.
A sequência como metodologia
As três fases não foram sequenciais por acidente. Cada uma habilita a seguinte:
Índices primeiro: revelam a capacidade real necessária. Sem índices, o servidor parece precisar de mais CPU do que realmente precisa.
Rightsizing segundo: com carga real conhecida, é seguro reduzir a instância.
RI terceiro: com o tamanho confirmado, é seguro travar o desconto por 1 ano.
Inverter qualquer par dessa sequência gera um dos três problemas:
RI antes de rightsize: RI travada no tamanho errado.
Rightsize antes de índices: instância menor sob carga de queries ineficientes → incidente.
RI antes de índices: desconto em uma instância superdimensionada que será reduzida.
Otimização de infraestrutura não é uma lista de ações — é uma sequência com gates de validação. O gate é o dado que confirma que a próxima ação é segura. Sem o gate, cada ação é um risco; com o gate, cada ação é uma consequência lógica da anterior.
Os índices que executamos e as métricas de CPU que geraram estão documentados em detalhe em um post separado sobre o diagnóstico técnico do RDS. Este post foca na estratégia e na sequência — o 'como fazer isso na sua empresa' — não no detalhe técnico de cada índice.
