Empresas como Notion, Slack e Discord passaram por isso e usaram a mesma técnica: o sharding. Estudei sobre o assunto e quero compartilhar com vocês de forma simples, afinal a melhor forma de aprender é ensinando.

Banco único vs vários bancos

O cenário: o banco não aguenta mais

Seu sistema cresceu, o banco ficou lento e as escritas começam a engasgar. O primeiro reflexo é comprar uma máquina maior, com mais CPU, mais memória e disco mais rápido. Isso se chama escala vertical, ou seja, crescer "para cima".

Porém, nem tudo são flores. A escala vertical tem um teto: em algum momento não existe máquina maior para comprar. Além disso, fica cada vez mais caro, e se essa única máquina cair, cai tudo junto. É o famoso ponto único de falha.

"Mas e as réplicas?"

Réplicas são cópias do banco e ajudam muito na leitura, porque espalham as consultas. Porém, toda escrita continua indo para um único banco principal. Se o gargalo é escrita ou volume de dados, réplica não resolve.

O que é sharding

Em vez de uma máquina maior, você usa várias máquinas, e cada uma guarda só uma parte dos dados. Cada parte é chamada de shard. Todas têm as mesmas tabelas, mas linhas diferentes. Ou seja, escala horizontal.

Por exemplo, um app com 3 milhões de usuários. A shard key (já explico o que é) é o id do usuário: ids de 1 a 1 milhão vão para o shard 1, de 1 a 2 milhões para o shard 2 e de 2 a 3 milhões para o shard 3.

Banco dividido em shards

É como uma biblioteca que lotou e abriu unidades: autores de A a M na unidade 1, de N a Z na unidade 2. Achar um livro pelo autor é fácil. Mas achar "todos os livros de 2020" obriga a visitar todas as unidades. Guarda essa ideia, porque ela é o coração dos problemas do sharding.

Sharding, particionamento e replicação

Três termos que confundem muito:

  • Particionamento: dividir uma tabela em pedaços, geralmente no mesmo servidor. Por exemplo, o partitioning nativo do PostgreSQL.
  • Sharding: dividir os dados entre servidores diferentes.
  • Replicação: fazer cópias iguais dos dados em vários servidores.

Ou seja, sharding ajuda a escalar escrita e volume, e replicação ajuda a escalar leitura e disponibilidade. E eles andam juntos: na prática, cada shard costuma ter as suas próprias réplicas. Um não substitui o outro.

Como o sistema sabe onde está cada dado?

Se os dados estão espalhados, alguém precisa encaminhar a consulta para o shard certo. Isso pode ficar:

  • No código da aplicação, que calcula o shard.
  • Num intermediário, como o Vitess, que nasceu no YouTube e é usado pelo Slack.
  • No próprio banco, como o sharding nativo do MongoDB ou o Citus no Postgres.

No MongoDB, por exemplo, a aplicação fala com o roteador mongos, que consulta os metadados nos config servers e manda a query para o shard certo.

A shard key: a decisão mais importante

A shard key é a coluna que decide em qual shard cada linha vai morar. Uma boa chave tem muitos valores diferentes, distribui os dados por igual e aparece em quase toda consulta. Por exemplo, o id do usuário.

Por faixa (range)

Ids de 1 a 1 milhão no shard A, de 1 a 2 milhões no shard B. É ótima para buscar intervalos. Mas se a chave for a data, toda escrita de hoje cai no mesmo shard, que fica sobrecarregado.

Sharding por faixa

Por hash

O sistema passa a chave por uma função matemática (algo como hash(user_id) % N) que espalha os dados por igual entre os shards. Distribui muito bem. Porém, buscar um intervalo vira perguntar para todos os shards e juntar a resposta.

Sharding por hash

Por região ou por cliente

Também dá para separar por região, deixando os dados perto do usuário, ou por cliente. Por exemplo, o Notion dividiu o banco pelo id do workspace. Assim, os dados de uma mesma empresa ficam juntos e quase toda consulta vai para um shard só.

Dica: shards lógicos

Uma dica que o Notion usou: crie muito mais shards "lógicos" do que máquinas. Eles começaram com 480 shards em 32 bancos. É como uma mudança: você empacota tudo em 480 caixas. Se trocar 32 armários por 64, move caixas inteiras, sem desempacotar livro por livro.

Os problemas

Lembra das unidades da biblioteca? Consultas que cruzam shards, como JOINs e relatórios gerais, precisam perguntar para todos e juntar o resultado na aplicação. E transações envolvendo vários shards ficam caras e complicadas.

Outros problemas:

  • Ids: o auto-incremento de cada shard pode repetir o mesmo número.
  • Shard "quente": um cliente muito ativo concentra a carga. O Discord conta isso no post sobre como guarda trilhões de mensagens.
  • Resharding: mudar os dados de shard com o sistema no ar é a parte mais arriscada.

Então quando usar?

Só depois de tentar o mais simples: melhorar queries e índices, colocar cache, réplicas de leitura e uma máquina maior. E quando o problema é escrita ou volume que não cabe mais num servidor. Sharding cedo demais é complexidade de graça.

Ou seja, sharding resolve o limite de um banco só, mas cobra em complexidade para sempre, na aplicação e na operação. E sempre que possível, prefira ferramentas que já fazem isso por você (Vitess, Citus, MongoDB) em vez de reinventar.

Conclusão

Apresentei apenas algo introdutório, recomendo se aprofundarem! Se você já é experiente, complementa nos comentários. Obrigado por ler até aqui!

Fontes