Software · Arquitetura & Engenharia de Dados
Bancos de Dados Globalmente Distribuídos: Particionamento e os 5 Níveis de Consistência no Azure Cosmos DB
Como conciliar replicação multi-região ativa e baixa latência sem violar o Teorema CAP através de modelos de consistência flexíveis e particionamento inteligente.
Neste artigo
A engenharia de bancos de dados relacionais clássicos baseia-se na premissa de um único nó mestre concentrando as gravações para assegurar transações estritas (ACID). No entanto, quando aplicações web ou móveis precisam atender milhões de usuários distribuídos pela Europa, América do Norte, Ásia e América Latina, a velocidade da luz torna-se um limite físico incontornável. Um pacote de rede viajando de São Paulo a Tóquio e retornando impõe uma latência de tráfego de pelo menos 250 a 300 milissegundos, inviabilizando experiências interativas em tempo real caso cada consulta precise viajar até o outro lado do planeta.
Para superar essa barreira, surgiram os bancos de dados globalmente distribuídos e multi-mestre, dos quais o Azure Cosmos DB é um dos expoentes mais maduros. O grande desafio dessa arquitetura não é apenas replicar dados entre continentes, mas gerenciar o compromisso fundamental expresso pelo Teorema CAP e pelo Teorema PACELC: na presença de partições de rede ou em operação normal, como equilibrar consistência de leitura, latência de resposta e custo financeiro?
O Espectro dos 5 Níveis de Consistência
Muitos bancos NoSQL distribuídos forçam uma escolha binária simplista: consistência forte (Strong Consistency, de alto custo e alta latência) ou consistência eventual (Eventual Consistency, rápida, porém com leituras de dados obsoletos).
O Cosmos DB resolve essa polarização ao oferecer um espectro granular de cinco níveis de consistência matematicamente bem definidos, permitindo que a arquitetura selecione o compromisso exato para cada carga de trabalho:
flowchart LR
Strong["1. Strong (Forte)<br/>Linearizabilidade global"] --> Bounded["2. Bounded Staleness<br/>Defasagem limitada (K versões ou T segundos)"]
Bounded --> Session["3. Session (Sessão)<br/>Read-your-own-writes (Padrão de 73% do mercado)"]
Session --> Prefix["4. Consistent Prefix<br/>Nunca lê atualizações fora de ordem"]
Prefix --> Eventual["5. Eventual (Eventual)<br/>Maior throughput e menor latência"]
1. Strong (Forte)
Oferece linearizabilidade completa. As leituras têm a garantia absoluta de sempre retornar a versão mais recente confirmada de um registro.
- Funcionamento: A gravação só é confirmada para o cliente após atingir um quórum de confirmação majoritário global entre todas as réplicas.
- Trade-off: Alta latência de escrita em contas multi-região e menor disponibilidade em caso de partições de rede globais.
2. Bounded Staleness (Defasagem Limitada)
Garante que as leituras possam ficar atrás das gravações no máximo por uma janela configurável: um intervalo de tempo fixo (ex: até 5 segundos) ou um número determinado de versões de registros (K updates).
- Trade-off: Excelente para aplicações que exigem leituras em ordem e dados muito frescos, mas não podem arcar com o custo de latência do quórum síncrono global.
3. Session (Sessão) - O Nível Padrão
Garante consistência do tipo read-your-own-writes e write-follows-reads dentro do escopo da mesma sessão de usuário (gerenciada por um Session Token nos cabeçalhos HTTP do SDK).
- Funcionamento: O usuário que acabou de publicar um comentário ou alterar seu perfil vê imediatamente a sua própria alteração, mesmo que outros usuários ao redor do mundo ainda estejam convergindo gradualmente.
- Trade-off: Proporciona a melhor relação entre consistência intuitiva para o usuário, latência de leitura submilisegundo e custo operacional eficiente.
4. Consistent Prefix (Prefixo Consistente)
Garante que o leitor nunca observe gravações fora de ordem. Se ocorreram as alterações A, B e C, um leitor pode ver A, ou A, B, mas jamais verá B antes de A ou C sem ter visto B.
5. Eventual (Eventual)
A forma mais fraca de consistência. Não há garantia de ordenação temporal ou frescor de leitura imediato; as réplicas convergem em segundo plano com o menor custo de computação possível.
Matriz de Decisão Arquitetural
| Nível de Consistência | Garantia de Leitura | Custo de RUs (Request Units) | Latência de Leitura | Latência de Escrita |
|---|---|---|---|---|
| Strong | Dados mais recentes em escala global | 2x RUs (Quórum síncrono) | Mais alta (espera quórum) | Mais alta (espera replicação global) |
| Bounded Staleness | Atraso máximo limitado por K ou T | 2x RUs (Quórum local) | Baixa (local à região) | Média |
| Session | Lê as próprias gravações na sessão | 1x RU | Mínima (< 10 ms local) | Baixa (< 10 ms local) |
| Consistent Prefix | Nunca lê dados fora de ordem | 1x RU | Mínima | Baixa |
| Eventual | Melhor esforço assíncrono | 1x RU | Mínima | Baixa |
Estratégias de Chave de Partição (Partition Keys)
A escalabilidade de um banco globalmente distribuído depende de como os dados são divididos em partições lógicas e partições físicas.
No Cosmos DB, cada documento gravado deve possuir uma PartitionKey. O hash desse atributo determina em qual partição física de 50 GB o registro residirá:
flowchart TD
Doc["Novo Documento (ex: Pedido do Cliente)"] --> Hash["Função Hash Interna da PartitionKey"]
Hash --> Range{"Intervalo de Hash do Particionamento"}
Range -->|"0000 - 3FFF"| P1["Partição Física 1 (Até 50 GB / 10.000 RUs)"]
Range -->|"4000 - 7FFF"| P2["Partição Física 2 (Até 50 GB / 10.000 RUs)"]
Range -->|"8000 - BFFF"| P3["Partição Física 3 (Até 50 GB / 10.000 RUs)"]
Range -->|"C000 - FFFF"| P4["Partição Física 4 (Até 50 GB / 10.000 RUs)"]
O Perigo das Hot Partitions
Escolher uma chave de partição com baixa cardinalidade (por exemplo, o campo Pais ou StatusPedido) é uma falha grave de desenho. Se 90% dos pedidos forem do Brasil, 90% das requisições atingirão a mesma partição física, gerando estrangulamento de throughput (Rate Limiting com erros HTTP 429), enquanto as outras partições físicas permanecem subutilizadas.
Boas práticas de escolha de Partition Key:
- Alta cardinalidade: Chaves com milhares ou milhões de valores distintos (ex:
TenantId,CustomerIdou uma chave sintética comoUserId_Data). - Padrão de acesso uniforme: O volume de leituras e gravações deve se espalhar uniformemente ao longo do tempo.
- Consultas com escopo de partição: As consultas SQL mais frequentes devem incluir a chave de partição no filtro
WHERE, permitindo que o roteador consulte apenas uma partição física em vez de varrer todas as partições do cluster (Cross-Partition Query).
Replicação Multi-Região e Resolução de Conflitos
Quando o modo de gravação multi-região (Multi-region Writes) é ativado, usuários na Europa gravam no datacenter da Europa Ocidental e usuários no Brasil gravam no datacenter de São Paulo simultaneamente.
Caso dois usuários atualizem o mesmo documento no mesmo milissegundo em regiões distintas, o Cosmos DB emprega uma política declarativa de resolução de conflitos:
- Last-Write-Wins (LWW): Padrão do sistema. A gravação com o maior carimbo de data/hora numérico (
_ts) vence, descartando a alteração anterior. - Custom / Stored Procedure: A aplicação registra uma rotina JavaScript ou função de mesclagem que resolve conflitos de campo a campo de forma determinística (por exemplo, somando valores de carrinho de compras).
- Conflict Feed: As versões divergentes são gravadas em uma pasta interna de conflitos para análise assíncrona manual ou lógica de compensação de negócio.