O que é fragmentação de banco de dados?

O particionamento de banco de dados é uma estratégia usada para resolver problemas de escalonabilidade em aplicativos que têm uma quantidade enorme de dados. Ele envolve a divisão de um único conjunto de dados lógico grande em partes menores e mais gerenciáveis chamadas de fragmentos. Cada fragmento é armazenado em uma instância de servidor de banco de dados separada, o que efetivamente distribui os dados e a carga de trabalho em várias máquinas.

A fragmentação é um método essencial para o escalonamento horizontal (expansão). Em vez de fazer upgrade de um único servidor com mais CPU ou RAM (escalonamento vertical), que acaba atingindo um limite de hardware, o particionamento permite adicionar mais servidores básicos ao cluster. Isso permite que os aplicativos lidem com um crescimento quase infinito no volume de dados e no tráfego de usuários.

Transformação do banco de dados da Credit Karma: do MySQL fragmentado ao Spanner

O particionamento é uma maneira eficiente de escalonar horizontalmente, mas é uma das três estratégias comuns de escalonamento horizontal:

  • Réplicas de leitura: envolve a criação de cópias do banco de dados principal para lidar com o tráfego somente leitura, o que reduz a carga no servidor principal
  • Fragmentação: distribui os dados e o tráfego de gravação por vários servidores independentes
  • Bancos de dados distribuídos: produtos como o Spanner lidam de maneira nativa com a distribuição e o escalonamento no próprio mecanismo de banco de dados, eliminando a necessidade de fragmentação manual

Como funciona a fragmentação de banco de dados?

O particionamento de banco de dados funciona agrupando dados com base em um valor específico chamado chave de partição. Uma chave de fragmento é uma coluna no seu banco de dados, como um ID de usuário, região do cliente ou número do pedido, que determina qual servidor vai armazenar uma linha específica de dados. Quando os dados são gravados no banco de dados, o sistema analisa essa chave para decidir onde os dados pertencem.

Para encontrar os dados mais tarde, o sistema precisa encaminhar a consulta para o local correto. Esse roteamento acontece de duas maneiras principais:

  • Camada de roteamento: as arquiteturas de fragmentação podem usar um balanceador de carga inteligente ou uma camada de proxy dedicada. Quando um aplicativo envia uma consulta, a camada de roteamento verifica a chave de fragmento, calcula qual fragmento contém esses dados e direciona o tráfego para esse servidor.
  • Lógica do lado do aplicativo: em alguns casos, como em jogos multiplayer ou apps em tempo real de alto desempenho, o próprio código do aplicativo contém a lógica de fragmentação. O código calcula o local correto do fragmento antes mesmo de enviar a solicitação, o que pode reduzir a latência ao remover um salto de rede extra.

Ao segmentar apenas o fragmento relevante, o banco de dados responde às consultas com mais rapidez e lida com milhares de solicitações simultâneas sem diminuir a velocidade.

Infográfico de fragmentação de dados

Métodos comuns de fragmentação

Aplicativos diferentes exigem lógicas diferentes para dividir os dados. O método escolhido determina como a "camada de roteamento" encontra seus dados.

Esse método usa uma fórmula matemática (uma função hash) na chave de fragmentação para atribuir dados. Por exemplo, o sistema pode calcular o ID do usuário (mod 4) para atribuir um usuário a 1 dos 4 servidores.

Embora as funções de hash ajudem a distribuir os dados de maneira consistente, elas só garantem uma distribuição uniforme se a chave de fragmentação tiver alta cardinalidade e baixo desvio de frequência. Se você escolher uma chave de fragmentação com um valor comum, como um "Sobrenome" em que "Silva" aparece 1.000 vezes mais do que "Pyne", a função hash enviará todos os registros "Silva" para o mesmo fragmento. Isso cria um "shard quente" apesar do uso de uma fórmula matemática.

Adicionar novos servidores também é difícil com esse método porque a fórmula muda, o que geralmente exige que você "reparticione" ou mova seus dados pelo novo cluster de servidores.

Os dados são atribuídos com base em intervalos de valores. Por exemplo, você pode colocar os IDs de usuário de 1 a 1.000 no servidor A e de 1.001 a 2.000 no servidor B. Isso é muito intuitivo e ótimo para consultas que precisam ler uma sequência de dados (consultas de intervalo). A desvantagem são os "pontos críticos": se todos os seus novos usuários forem atribuídos ao servidor B, ele fará todo o trabalho enquanto o servidor A ficar ocioso.

Essa estratégia usa uma tabela de pesquisa (um diretório) que rastreia exatamente qual fragmento contém quais dados. Ela oferece mais flexibilidade porque você pode mover dados entre fragmentos sem mudar uma fórmula. No entanto, essa tabela de pesquisa se torna um gargalo: toda consulta precisa verificar o diretório primeiro, o que aumenta a latência. Se o diretório falhar, todo o banco de dados ficará inacessível.

O particionamento geográfico atribui dados a servidores específicos com base na localização física de um usuário. Por exemplo, os dados dos usuários na França são armazenados em servidores na UE, enquanto os usuários dos EUA estão em servidores na América do Norte. Isso reduz significativamente a latência (velocidade) para os usuários e ajuda as empresas a cumprir leis de residência de dados como o GDPR.

Às vezes chamado de particionamento funcional, isso envolve dividir os dados por recurso em vez de por linha. Por exemplo, você pode colocar todas as tabelas de "Perfil do usuário" no Servidor A e todas as tabelas de "Upload de fotos" no Servidor B. Embora isso organize os dados de maneira lógica, é funcionalmente semelhante a uma arquitetura de dados de microsserviços e não resolve o problema se um recurso específico (como o Fotos) crescer demais para um único servidor.

Como otimizar a fragmentação de banco de dados para uma distribuição uniforme dos dados

Escolher a chave de fragmentação certa é a decisão mais importante na fragmentação. Uma chave ruim pode levar a uma distribuição desigual de dados (pontos de acesso), enquanto uma boa chave pode garantir que todos os servidores funcionem igualmente. Para otimizar isso, você precisa analisar três fatores:

  1. Cardinalidade: refere-se ao número de valores exclusivos que uma chave pode ter. Você quer uma chave com alta cardinalidade (como um ID de usuário), não baixa cardinalidade (como "Gênero" ou "Estado"), para que os dados possam ser divididos em muitos pequenos blocos.
  2. Frequência: evite chaves em que um valor específico aparece com muita frequência. Por exemplo, se você fizer o particionamento por "Cidade" e 80% dos seus usuários morarem em Nova York, a partição "Nova York" ficará sobrecarregada.
  3. Mudança monotônica: evite chaves que aumentam estritamente ao longo do tempo, como um carimbo de data/hora ou um ID de incremento automático. Se você particionar por data, todas as novas gravações de dados vão para a partição "Hoje", criando um ponto de acesso de gravação enquanto as partições mais antigas ficam ociosas.

Principais termos de fragmentação

Embora esses termos sejam frequentemente usados juntos no design de sistemas, eles resolvem problemas diferentes.

O fragmentação é um tipo específico de particionamento horizontal em que os dados são distribuídos em servidores completamente diferentes. Isso resolve os limites de capacidade de hardware (armazenamento) e os gargalos de capacidade de gravação, já que diferentes servidores podem processar gravações simultaneamente.

  • Fragmentação manual: em um ambiente de fragmentação manual, o desenvolvedor ou administrador de banco de dados é responsável por definir as chaves de fragmentação, criar a infraestrutura para cada fragmento e escrever a lógica para rotear consultas. Isso oferece controle granular sobre o local exato dos dados. No entanto, ela exige muita manutenção. Se um fragmento ficar muito grande, você precisa "reparticionar" os dados manualmente, o que envolve mover milhões de linhas para novos servidores e tentar minimizar o tempo de inatividade do aplicativo.
  • Fragmentação automática: a fragmentação automática, que geralmente é encontrada em bancos de dados NoSQL como o Bigtable ou em bancos de dados SQL distribuídos como o Spanner, lida com a distribuição de dados e tráfego automaticamente. O sistema monitora o tamanho dos dados e a carga em cada servidor. Se um fragmento específico se tornar um "ponto de acesso" ou crescer demais, o mecanismo de banco de dados vai dividir automaticamente o fragmento e mover os dados para um servidor menos congestionado. Isso reduz a carga operacional da sua equipe e ajuda a garantir um desempenho consistente à medida que seu aplicativo é escalonado.

O particionamento envolve dividir uma tabela grande em partes menores e mais gerenciáveis (como dividir uma tabela de registros por mês), mas mantendo-as na mesma instância de servidor. Isso resolve problemas de armazenamento, facilitando o arquivamento ou a exclusão de dados antigos sem afetar o restante da tabela. No entanto, isso não resolve problemas do servidor. Como todas as partições ainda residem em uma única máquina, elas continuam a compartilhar a mesma CPU e RAM, o que significa que o particionamento não ajuda se o servidor atingir seus limites de desempenho.

A replicação é o processo de copiar todo o banco de dados para vários servidores. Essa pode ser uma boa opção para disponibilidade de leitura. Se um servidor falhar, outro pode assumir. No entanto, ela não ajuda no escalonamento de gravação, porque cada dado gravado precisa ser copiado para cada réplica, limitando a velocidade de gravação à capacidade de uma única máquina.

Além disso, a maioria dos modelos de replicação permite apenas um "gravador" (o nó principal) por vez. Se você tentar permitir que vários servidores aceitem gravações simultaneamente, corre o risco de conflitos de gravação, em que dois servidores tentam atualizar o mesmo registro com informações diferentes. Resolver esses conflitos é tecnicamente difícil e pode levar à perda ou inconsistência de dados se não for tratado por um sistema distribuído sofisticado.

Um banco de dados distribuído, como o Spanner, oferece os benefícios do particionamento sem a sobrecarga manual. Esses sistemas são projetados para funcionar em um cluster de máquinas desde o início. Eles lidam automaticamente com a distribuição, o rebalanceamento e a replicação de dados de maneira transparente. Alguns desses sistemas têm vários gravadores e lidam automaticamente com conflitos de gravação. Isso permite que você faça o escalonamento horizontal sem perder a consistência de um banco de dados relacional tradicional.

Use a tabela abaixo para entender as diferenças entre esses conceitos básicos de banco de dados.

Recurso

Fragmentação

Particionamento

Replicação

Banco de dados distribuído

Meta principal

Escalonabilidade e armazenamento de gravação em massa

Gerenciamento e manutenção

Alta disponibilidade e escalonamento de leitura


Escalonamento global automatizado

Local dos dados

Diferentes partes de dados em servidores diferentes

Diferentes partes de dados no mesmo servidor

Cópias dos mesmos dados em vários servidores

Gerenciados em um cluster

Desempenho de gravação

Melhoria alta (as gravações acontecem em paralelo)

Melhoria pequena (índices menores)


Nenhuma melhoria (as gravações precisam ir para todas as cópias)

Melhoria alta

Complexidade

Alta

Média

Baixa

Baixa (gerenciada)

Recurso

Fragmentação

Particionamento

Replicação

Banco de dados distribuído

Meta principal

Escalonabilidade e armazenamento de gravação em massa

Gerenciamento e manutenção

Alta disponibilidade e escalonamento de leitura


Escalonamento global automatizado

Local dos dados

Diferentes partes de dados em servidores diferentes

Diferentes partes de dados no mesmo servidor

Cópias dos mesmos dados em vários servidores

Gerenciados em um cluster

Desempenho de gravação

Melhoria alta (as gravações acontecem em paralelo)

Melhoria pequena (índices menores)


Nenhuma melhoria (as gravações precisam ir para todas as cópias)

Melhoria alta

Complexidade

Alta

Média

Baixa

Baixa (gerenciada)

Benefícios da fragmentação de banco de dados

A fragmentação costuma ser a única solução viável para aplicativos que lidam com terabytes de dados ou milhões de transações por segundo.

Escalonamento horizontal

A fragmentação permite um escalonamento quase infinito ao adicionar servidores padrão a um cluster. Isso evita o "tributos de hardware" de aplicativos legados com escalonamento vertical. Sem a fragmentação, você costuma ser obrigado a comprar hardware caro e especializado que atinge um limite de desempenho. A fragmentação permite que o banco de dados cresça junto com sua empresa usando máquinas mais acessíveis e comuns

Melhoria no desempenho das consultas

A fragmentação acelera consultas individuais porque cada servidor pesquisa um conjunto de dados menor. Em vez de pesquisar um índice de 100 milhões de linhas, uma consulta pode precisar pesquisar apenas um fragmento com 1 milhão de linhas. Além disso, como os dados estão em máquinas diferentes, é possível executar várias consultas em paralelo, o que aumenta muito a capacidade de processamento total do aplicativo.

Confiabilidade

A fragmentação limita o "raio de impacto" de uma falha. Se um fragmento falhar, apenas esses usuários serão afetados, enquanto o restante do app permanece on-line. No entanto, mais servidores significam uma maior carga administrativa. Gerenciar backups, segurança e patches em dezenas de instâncias aumenta a complexidade operacional em comparação com uma configuração de servidor único.

Desafios da fragmentação

Embora a fragmentação resolva requisitos de escala massiva, ele introduz compensações técnicas e operacionais significativas. Considere esses desafios antes de abandonar uma arquitetura de instância única.

  • Pontos de acesso de dados: mesmo com uma função hash, o tráfego desigual pode criar "pontos de acesso" em que um servidor fica sobrecarregado por um pequeno grupo de usuários altamente ativos, enquanto outros fragmentos permanecem subutilizados.
  • Regressão de desempenho: consultas que não usam a chave de fragmento exigem que o sistema faça uma coleta e dispersão em todos os servidores. Da mesma forma, as junções entre fragmentos são caras do ponto de vista computacional porque o aplicativo precisa extrair e mesclar dados de vários locais físicos.
  • Complexidade operacional: gerenciar um ambiente fragmentado aumenta a dificuldade das tarefas de rotina. As atualizações de esquema, os backups e a recuperação pontual precisam ser coordenados em várias instâncias independentes.
  • Consistência transacional: muitas arquiteturas fragmentadas têm dificuldade em oferecer suporte a transações ACID em diferentes fragmentos. Isso muitas vezes força os desenvolvedores a gerenciar cenários complexos de "falha parcial" ou a confiar em modelos de consistência posterior.

Resolva seus desafios comerciais com o Google Cloud

Clientes novos recebem US$ 300 em créditos para usar no Google Cloud.

Como o Google Cloud pode ajudar com a fragmentação de banco de dados?

O Google Cloud oferece soluções de banco de dados que eliminam o trabalho pesado da fragmentação manual, permitindo que você se concentre na criação do seu aplicativo em vez de gerenciar a infraestrutura.


Vá além

Tudo pronto para parar de se preocupar com os limites do banco de dados?

Google Cloud