Sharding de banco de dados: horizontal scaling na prática
1. Fundamentos do Sharding e quando aplicá-lo
Sharding é uma técnica de particionamento horizontal que divide um banco de dados grande em partes menores e independentes chamadas shards. Cada shard contém um subconjunto dos dados e opera como um banco de dados separado. Diferente do particionamento vertical (que separa colunas), o sharding distribui linhas entre diferentes servidores.
Os sinais clássicos de que você precisa de sharding incluem:
- Gargalos de escrita em um único servidor
- Crescimento exponencial de dados que ultrapassa a capacidade de armazenamento
- Queries lentas mesmo após otimizações de índice
- Impossibilidade de manter o banco inteiro em memória
É importante distinguir sharding de replicação (cópias completas dos dados para leitura) e particionamento de tabelas (divisão lógica dentro do mesmo servidor). Sharding resolve problemas de escala horizontal, enquanto replicação foca em disponibilidade e particionamento em organização.
2. Estratégias de chave de shard e distribuição de dados
Sharding por range
Os dados são divididos por faixas de valores da chave de shard. Por exemplo, usuários com IDs 1-1000 no shard A, 1001-2000 no shard B.
Shard A: usuarios_id 1 a 1000
Shard B: usuarios_id 1001 a 2000
Shard C: usuarios_id 2001 a 3000
Vantagem: Queries por range são eficientes.
Risco: Hotspots — se novos registros caem sempre no mesmo range.
Sharding por hash
Uma função hash é aplicada à chave de shard para determinar o destino. Exemplo: shard_id = hash(chave) % N.
hash("usuario_123") % 4 = 2 → Shard 2
hash("usuario_456") % 4 = 0 → Shard 0
Vantagem: Distribuição uniforme.
Desvantagem: Adicionar shards exige rehash de todos os dados.
Sharding baseado em diretório
Uma tabela de lookup mapeia chaves para shards.
Tabela lookup:
chave: "cliente_A" → shard: 1
chave: "cliente_B" → shard: 3
Vantagem: Flexibilidade total para mover dados.
Desvantagem: Ponto único de falha e latência extra.
3. Arquiteturas de roteamento de queries
Roteamento no cliente
A aplicação sabe em qual shard cada dado reside e envia queries diretamente.
# Exemplo conceitual em aplicação
def buscar_usuario(user_id):
shard = hash(user_id) % 4
return conectar_banco(shard).query("SELECT * FROM usuarios WHERE id=?", user_id)
Proxy de banco de dados
Um middleware como Vitess ou ProxySQL intercepta queries e roteia automaticamente.
Cliente → ProxySQL → Shard 1
→ Shard 2
→ Shard 3
Desafios: Queries cross-shard exigem scatter-gather (enviar para todos os shards e combinar resultados). Joins distribuídos são particularmente custosos e devem ser evitados.
4. Gerenciamento de transações e consistência em ambientes sharded
Transações distribuídas
- 2PC (Two-Phase Commit): Garante consistência forte, mas com alta latência e risco de bloqueio.
- SAGA: Quebra a transação em etapas com compensações, adequada para microserviços.
Exemplo SAGA:
1. Reservar estoque no Shard A
2. Cobrar cartão no Shard B
3. Se falha na etapa 2 → reverter etapa 1
Trade-offs
- Consistência forte é cara em ambientes sharded.
- Consistência eventual é mais escalável, mas exige lógica de aplicação para lidar com dados temporariamente inconsistentes.
Estratégias para evitar deadlocks:
- Acessar shards sempre na mesma ordem
- Usar locks com timeout
- Minimizar transações multi-shard
5. Rebalanceamento de shards e escalabilidade dinâmica
Adicionar novos shards sem downtime é um dos maiores desafios. Técnicas comuns:
Consistent Hashing
Minimiza o movimento de dados quando shards são adicionados ou removidos.
Anel de hash:
Shard A: posições 0-25, 50-75
Shard B: posições 25-50, 75-100
Novo Shard C: assume parte das posições de A e B
Movimento em lote
Dados são migrados em lotes pequenos durante períodos de baixa atividade.
1. Criar novo shard como réplica
2. Sincronizar dados incrementalmente
3. Atualizar tabela de lookup
4. Remover dados do shard antigo
Monitoramento de hotspots: Métricas como IOPS por shard, tamanho de dados e latência de queries ajudam a identificar shards sobrecarregados.
6. Backup, recovery e disaster recovery em ambientes sharded
Backup consistente
Cada shard deve ser tratado como um banco independente. Backups simultâneos podem gerar inconsistências entre shards.
Estratégia recomendada:
1. Pausar escritas (ou usar snapshot consistente global)
2. Backup de cada shard individualmente
3. Registrar timestamp do backup global
Restauração ponto-a-ponto
Cada shard pode ser restaurado independentemente, desde que o ponto de restauração seja consistente entre todos.
1. Restaurar Shard A do backup das 03:00
2. Restaurar Shard B do backup das 03:00
3. Aplicar logs até o timestamp desejado em cada shard
Testes de failover
Simular perda de um shard e verificar se o sistema se recupera sem perda de dados.
7. Boas práticas operacionais e armadilhas comuns
Evitando shard skew
Monitore a distribuição de dados e carga. Um shard com 80% dos dados ativos pode tornar o sharding inútil.
Cuidados com chaves de shard mutáveis
Se a chave de shard pode mudar (ex.: email do usuário), você precisará mover dados entre shards — operação complexa e arriscada.
Documentação e automação
Documente a topologia de shards, chaves de shard e procedimentos de manutenção. Automatize tarefas como:
- Adição de novos shards
- Rebalanceamento
- Backup e recovery
Checklist operacional:
[ ] Monitoramento de latência por shard
[ ] Alertas para skew de dados (>20% de diferença)
[ ] Testes trimestrais de failover
[ ] Documentação atualizada da topologia
Referências
- MySQL Sharding — Documentação Oficial — Guia oficial sobre implementação de sharding no MySQL, incluindo estratégias e limitações.
- Vitess: Sharding no Kubernetes — Documentação do Vitess, middleware de sharding open-source usado em escala de produção no YouTube.
- PostgreSQL Sharding com Citus — Tutorial prático de sharding horizontal no PostgreSQL usando a extensão Citus.
- Consistent Hashing: Técnica de Rebalanceamento — Artigo técnico explicando consistent hashing e sua aplicação em sistemas distribuídos.
- Transações Distribuídas: SAGA vs 2PC — Padrões de transações distribuídas para ambientes sharded, com exemplos de implementação.
- ProxySQL: Roteamento de Queries em Shards — Documentação oficial do ProxySQL para roteamento inteligente de queries em ambientes sharded.