Estratégias de escalonamento de banco de dados: leitura de réplicas, fragmentação e muito mais

Dimensione seu banco de dados com réplicas de leitura, fragmentação horizontal, pool de conexões e estratégias de cache. Abrange PostgreSQL, MySQL e serviços de banco de dados gerenciados.

E
ECOSIRE Research and Development Team
|16 de março de 20268 min de leitura1.7k Palavras|

Estratégias de escalonamento de banco de dados: leitura de réplicas, fragmentação e muito mais

O desempenho do banco de dados é o gargalo em 78% dos problemas de dimensionamento de aplicativos Web. Os aplicativos podem ser dimensionados horizontalmente com esforço mínimo, mas os bancos de dados resistem ao dimensionamento horizontal. As estratégias escolhidas para o dimensionamento do banco de dados determinam se seu aplicativo atende 100 usuários ou 100.000 usuários com desempenho aceitável.

Este guia cobre o espectro completo de estratégias de escalonamento de banco de dados, desde otimizações simples que atrasam a necessidade de escalonamento até técnicas avançadas, como fragmentação horizontal.

Principais conclusões

  • Otimize consultas e adicione índices antes de adicionar infraestrutura --- isso resolve 60% dos problemas de desempenho do banco de dados
  • As réplicas de leitura são a estratégia de escalonamento de menor risco e lidam com 80% das cargas de trabalho com uso intenso de leitura
  • O pool de conexões é obrigatório quando seu aplicativo executa mais de 10 instâncias
  • A fragmentação horizontal é um último recurso que introduz uma complexidade significativa de aplicação

A escada de escala

Dimensione nesta ordem. Cada etapa é mais barata e menos arriscada que a seguinte:

Etapa 1: Otimização de consulta (gratuita)

Antes de adicionar infraestrutura, certifique-se de que seu banco de dados existente esteja funcionando de maneira ideal.

-- Find slow queries in PostgreSQL
SELECT
  calls,
  mean_exec_time::numeric(10,2) AS avg_ms,
  total_exec_time::numeric(10,2) AS total_ms,
  query
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 20;

Otimizações comuns:

  • Adicione índices ausentes para colunas filtradas com frequência
  • Substitua SELECT * por listas de colunas específicas
  • Use EXPLAIN ANALYZE para identificar varreduras sequenciais em tabelas grandes
  • Adicionar índices compostos para cláusulas WHERE de várias colunas
  • Implementar paginação com paginação de conjunto de chaves em vez de OFFSET
-- Bad: OFFSET pagination (scans all skipped rows)
SELECT * FROM orders ORDER BY created_at DESC LIMIT 20 OFFSET 10000;

-- Good: Keyset pagination (index-only scan)
SELECT * FROM orders
WHERE created_at < '2026-03-15T10:00:00Z'
ORDER BY created_at DESC
LIMIT 20;

Etapa 2: Escala vertical ($)

Aumente a CPU, a RAM e o armazenamento no seu servidor de banco de dados existente. Isso ganha tempo e não requer nenhuma alteração no aplicativo.

Tamanho da instânciavCPUmemória RAMConexõesCusto Mensal (RDS)
db.t3.médio24 GB100US$ 65
db.r6g.large216 GB200US$ 175
db.r6g.xlarge432 GB400US$ 350
db.r6g.2xlarge864 GB800US$ 700

A maioria dos aplicativos atinge seu limite de 64 GB de RAM e 8 vCPUs. Além disso, a escala vertical torna-se proibitiva em termos de custos.

Etapa 3: Pool de conexões ($)

Application (50 pods x 20 connections = 1,000 connections)
    |
    v
PgBouncer (25 database connections, transaction pooling)
    |
    v
PostgreSQL (25 active connections, manageable)

Configuração do PgBouncer:

[databases]
app = host=db.example.com port=5432 dbname=production

[pgbouncer]
listen_port = 6432
listen_addr = 0.0.0.0
auth_type = md5
pool_mode = transaction
default_pool_size = 25
max_client_conn = 1000
min_pool_size = 5
reserve_pool_size = 5
reserve_pool_timeout = 3

Etapa 4: Ler réplicas ($$)

As réplicas de leitura lidam com consultas SELECT, descarregando de 60 a 90% da carga do banco de dados do primário.

Arquitetura:

Write queries --> Primary database
                      |
              Replication (async)
                      |
                 +----+----+
                 |         |
Read queries --> Replica 1  Replica 2

Roteamento em nível de aplicativo (exemplo Drizzle ORM):

import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';

const primaryPool = new Pool({ connectionString: process.env.DATABASE_URL });
const replicaPool = new Pool({ connectionString: process.env.DATABASE_REPLICA_URL });

export const primaryDb = drizzle(primaryPool);
export const replicaDb = drizzle(replicaPool);

// In service code:
// Write operations use primaryDb
async createOrder(data: OrderInput) {
  return primaryDb.insert(orders).values(data).returning();
}

// Read operations use replicaDb
async getOrders(organizationId: string) {
  return replicaDb.select().from(orders)
    .where(eq(orders.organizationId, organizationId))
    .orderBy(desc(orders.createdAt));
}

Considerações sobre atraso de replicação: a replicação assíncrona introduz um atraso (normalmente de 10 a 100 ms). Imediatamente após uma gravação, a leitura da réplica pode retornar dados obsoletos. Use o primário para leituras que seguem gravações no mesmo fluxo de usuário.

Etapa 5: Cache ($$)

O cache do Redis elimina totalmente as consultas repetidas ao banco de dados.

async getProduct(id: string): Promise&lt;Product> {
  const cacheKey = `product:${id}`;

  // Check cache first
  const cached = await this.redis.get(cacheKey);
  if (cached) return JSON.parse(cached);

  // Cache miss: query database
  const product = await this.db.select().from(products)
    .where(eq(products.id, id))
    .limit(1);

  // Cache for 5 minutes
  await this.redis.setex(cacheKey, 300, JSON.stringify(product[0]));

  return product[0];
}

Estratégia de invalidação de cache: invalidar na gravação. Quando um produto for atualizado, exclua a chave de cache. Use um padrão de cache-aside (o aplicativo gerencia o cache) em vez de write-through (o banco de dados gerencia o cache).

Etapa 6: Fragmentação horizontal ($$$)

A fragmentação distribui dados entre várias instâncias de banco de dados com base em uma chave de fragmento.

Estratégia de fragmentaçãoDescriçãoMelhor para
Baseado em hashHash a chave de fragmento, distribua uniformementeDistribuição uniforme de dados
Baseado em intervaloAtribuir intervalos aos fragmentos (por exemplo, AM, NZ)Séries temporais, dados geográficos
Baseado em inquilinoUm fragmento por locatário/organizaçãoSaaS multilocatário

Quando fragmentar:

  • Banco de dados único excede 1 TB e está crescendo
  • A taxa de transferência de gravação excede o que um único primário pode suportar
  • Os custos de escala vertical excedem US$ 2.000/mês sem margem de manobra

Quando NÃO fragmentar:

  • Você não esgotou as etapas 1 a 5
  • Seus dados cabem em um único banco de dados de 500 GB
  • Consultas entre fragmentos são comuns em seu aplicativo

Otimizações específicas do PostgreSQL

Particionamento (antes da fragmentação)

O particionamento de tabelas do PostgreSQL divide tabelas grandes em tabelas físicas menores, mantendo uma única tabela lógica:

-- Partition orders by month
CREATE TABLE orders (
    id UUID PRIMARY KEY,
    organization_id UUID NOT NULL,
    created_at TIMESTAMP NOT NULL,
    total DECIMAL(10,2)
) PARTITION BY RANGE (created_at);

CREATE TABLE orders_2026_01 PARTITION OF orders
    FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');

CREATE TABLE orders_2026_02 PARTITION OF orders
    FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');

CREATE TABLE orders_2026_03 PARTITION OF orders
    FOR VALUES FROM ('2026-03-01') TO ('2026-04-01');

O particionamento melhora o desempenho de consultas de intervalo de tempo em 10 a 100x em tabelas grandes porque o PostgreSQL verifica apenas partições relevantes.

Aspiração e Manutenção

-- Check table bloat
SELECT
  schemaname,
  relname,
  n_live_tup,
  n_dead_tup,
  round(n_dead_tup::numeric / greatest(n_live_tup, 1) * 100, 2) AS dead_pct
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC;

Configure o autovacuum agressivamente para tabelas com alta gravação:

ALTER TABLE orders SET (
  autovacuum_vacuum_threshold = 100,
  autovacuum_vacuum_scale_factor = 0.05,
  autovacuum_analyze_threshold = 50,
  autovacuum_analyze_scale_factor = 0.02
);

Monitorando o desempenho do banco de dados

Acompanhe essas métricas para entender quando e como escalar:

MétricaFerramentaLimite de alerta
Latência de consulta (P95)pg_stat_statements>500ms
Conexões ativaspg_stat_atividade>80% do máximo
Proporção de acertos de cachepg_stat_database<95%
Atraso na replicaçãopg_stat_replicação>1 segundo
Inchaço de mesapg_stat_user_tables>20% de tuplas mortas
Espera de E/S de discoiostat/CloudWatch>20ms

Uma taxa de acertos de cache abaixo de 95% é o indicador mais forte de que você precisa de mais memória. Aumentar shared_buffers e effective_cache_size costuma ser mais barato e rápido do que adicionar réplicas de leitura.

Acompanhamento de desempenho de consulta

-- Enable pg_stat_statements (postgresql.conf)
-- shared_preload_libraries = 'pg_stat_statements'

-- Find the top 10 most time-consuming queries
SELECT
  queryid,
  calls,
  mean_exec_time::numeric(10,2) AS avg_ms,
  total_exec_time::numeric(10,2) AS total_ms,
  rows,
  query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;

Revise as 10 principais consultas semanalmente. Otimizar até mesmo uma única consulta executada com frequência pode reduzir a carga geral do banco de dados em 10 a 30%.


Perguntas frequentes

Como sabemos quando é hora de escalar?

Monitore três métricas: latência de consulta P95 (alerta em 500ms), utilização de conexão (alerta em 80%) e utilização de CPU (alerta em 70% sustentado). Se você atinge esses limites regularmente, passe para o próximo degrau na escada de escala. Não pré-otimize --- dimensione quando os dados solicitarem.

Ler réplicas ou cache --- qual primeiro?

Comece com o cache. O cache Redis é mais simples de implementar, elimina mais carga (as ocorrências de cache ignoram totalmente o banco de dados) e custa menos. Adicione réplicas de leitura quando a taxa de acertos do cache já estiver acima de 80%, mas o banco de dados primário ainda estiver sob pressão por falhas de cache e operações de gravação.

Como funciona o escalonamento de banco de dados com Odoo?

Odoo usa exclusivamente PostgreSQL. Comece com a otimização de consultas (Odoo gera consultas complexas para relatórios). Adicione PgBouncer para pool de conexões quando você exceder 50 usuários simultâneos. Use réplicas de leitura para consultas de relatórios (configure a opção --db-replica do Odoo). ECOSIRE fornece otimização de desempenho Odoo incluindo ajuste de banco de dados.

O banco de dados gerenciado (RDS/Cloud SQL) vale o prêmio?

Sim, para a maioria das empresas. Os bancos de dados gerenciados lidam com backups, patches, failover e monitoramento automatizados. O custo adicional de 30-40% em relação ao PostgreSQL autogerenciado é compensado pelo tempo de engenharia que você economiza. A exceção são as implantações em larga escala, nas quais o custo adicional de uma instância grande excede o custo de um DBA de meio período.


O que vem a seguir

O dimensionamento de banco de dados é um componente de uma estratégia mais ampla de dimensionamento de infraestrutura. Combine-o com otimização de CDN para ativos estáticos, escalonamento automático do Kubernetes para pods de aplicativos e teste de carga para validar suas decisões de escalonamento em condições realistas.

Entre em contato com a ECOSIRE para obter consultoria de otimização de banco de dados ou consulte nosso guia DevOps para obter o roteiro completo de infraestrutura.


Publicado pela ECOSIRE – ajudando as empresas a dimensionar a infraestrutura de dados com confiança.

E

Escrito por

ECOSIRE Research and Development Team

Construindo produtos digitais de nível empresarial na ECOSIRE. Compartilhando insights sobre integrações Odoo, automação de e-commerce e soluções de negócios com IA.

Converse no WhatsApp