DEV Community

Henrique Reis
Henrique Reis

Posted on Edited on

[SD] Particionamento e Sharding: conceitos, estratégias e desafios na escalabilidade de bancos de dados

Agora vamos entrar no segundo grande mecanismo de distribuição de dados: sharding, também conhecido como particionamento quando estamos distribuindo essas partições entre diferentes nós.

No post anterior, vimos que a replicação cria cópias dos dados. Aqui a ideia é diferente: é dividir os dados para que cada nó seja responsável por uma parte deles. Isso é especialmente importante quando o volume de dados ou a quantidade de operações de escrita ultrapassa a capacidade de uma única máquina.


1. O que é Particionamento e Sharding?

Particionamento: Consiste em dividir um conjunto de dados em partes menores chamadas partições e distribuí-las entre diferentes nós (shards).

Imagine uma tabela com milhões de usuários:

Users
User 1
User 2
User 3
...
User 10.000.000

Em vez de manter todos esses registros em um único nó, podemos dividi-los:

imagem1

Cada partição contém apenas uma parte dos dados.

O objetivo é distribuir armazenamento e processamento entre diferentes nós.

Sharding: É uma forma de particionamento em que os dados são distribuídos entre diferentes nós físicos ou lógicos, chamados de shards

imagem10

Cada Shard é responsável por uma parte dos dados, por exemplo:

Shard 1 => usuários de A até F
Shard 2 => usuários de G até M
Shard 3 => usuários de N até Z
Enter fullscreen mode Exit fullscreen mode
  • Particionamento: É a ação lógica de cortar/dividir o seu grande conjunto de dados em pedaços menores (partições)
  • Shards: São as instâncias físicas, servidores ou nós de banco de dados onde esses pedaços serão armazenados.

O ponto importante é que nenhum nó precisa armazenar todos os usuários. Isso permite que o sistema cresça adicionando novos shards para distribuir o volume de dados e o processamento, evitando a sobrecarga dos nós existentes à medida que a base de usuários cresce.


2. Sharding não é replicação

Essa diferença precisa continuar muito clara, na replicação:

Node 1:
A B C D

Node 2:
A B C D
Enter fullscreen mode Exit fullscreen mode

Os dois nós possuem os mesmos dados.

No particionamento:

Node 1:
A B

Node 2:
C D
Enter fullscreen mode Exit fullscreen mode

Cada nó possui uma parte diferente dos dados.

Podemos utilizar os dois juntos:

imagem2

Nesse cenário, os dados são primeiro divididos em partições e depois cada partição pode possuir réplicas.

Portanto, eles resolvem problemas diferentes:

Replica => criar redundânmcia e cópias dos dados.
Sharding => distribuir os dados e a carga entre diferentes nós.


3. Por que precisamos particionar?

Imagine que uma tabela possui alguns terabytes de dados.

Mesmo utilizando uma máquina muito poderosa, existe um limite físico para armazenamento, memória, CPU e capacidade de I/O.

Podemos tentar aumentar a máquina virtualmente, mas eventualmente essa estratégia chega aos seus limites.

Com particionamento:

imagem3

Agora podemos distribuir o armazenamento entre diferentes máquinas.

Além disso, operações que anteriormente competiam pelos recursos de um único nó podem ser distribuídas. Esse é um dos principais motivos para utilizar sharding.


4. A Partition Key

Para sabe qual partição um determinado registro deve ficar, precisamos de um critério.

Esse critério normalmente é chamado de Partition Key.

Imagine:

User
id = 12345
Enter fullscreen mode Exit fullscreen mode

Podemos utilizar o userId como Partition Key.

O sistema utiliza essa chave para determinar onde o registro será armazenado.

De maneira simplificada:

imagem4

Portanto, a Partition Key possui uma importância enorme porque influencia como os dados serão distribuídos.

Uma escolha ruim pode fazer com que grande parte dos dados ou das requisições fique concnetrada em um único shard.


5. O que seria uma boa Partition Key?

Uma boa Partition Key deve, em geral, proporcionar uma distribuição equilibrada dos dados e das operações.

Imagine que temos:

userId = 1
userId = 2
userId = 3
...
Enter fullscreen mode Exit fullscreen mode

Se nossa estratégia distribuir esses usuários adequadamente:

Shard 1 => 33%
Shard 2 => 33%
Shard 3 => 33%
Enter fullscreen mode Exit fullscreen mode

Temos uma distribuição relativamente equilibrada. Mas imagine que escolhemos uma chave que concentra quase todos os registros em um único valor:

country = Brasil
Enter fullscreen mode Exit fullscreen mode

Se a maior parte dos usuários estiver no Brasil, podemos acabar com:

Shard 1 => 33%
Shard 2 => 33%
Shard 3 => 33%
Enter fullscreen mode Exit fullscreen mode

Isso é ruim porque o ssitema não está realmente distribuindo a carga de maneira equilibrada.


6. Hash Sharding

Uma estratégia bastante utilizada é o Hash Sharding

Nesse modelo, aplicamos uma função de hash à Partition Key:

imagem5

De maneira simplificada:

hash(userId) % numero_de_shards
Enter fullscreen mode Exit fullscreen mode

O resultado determina em qual shard o dado será armazenado.

Por exemplo:

User 101 => Hash => Shard 2
User 102 => Hash => Shard 1
User 103 => Hash => Shard 3
User 104 => Hash => Shard 2
Enter fullscreen mode Exit fullscreen mode

A principal vantagem é conseguir distribuição relativamente uniforme dos dados quando a chave possui boa variedade.


7. Range Sharding

Outra estratégia é o Range Sharding, onde os dados são divididos de acordo com intervalos da chave.

Por exemplo:

Shard 1 => IDs 1 - 1.000.000
Shard 2 => IDs 1.000.001 - 2.000.000
Shard 3 => IDs 2.000.001 - 3.000.000
Enter fullscreen mode Exit fullscreen mode

Isso facilita determinadas consultas que trabalham com intervalos.

Imagine uma consulta:

WHERE userId BETWEEN 1.000.000 AND 1.500.000
Enter fullscreen mode Exit fullscreen mode

Podemos identificar que os dados estão concentrados em determinados shards. Porém, o Range Sharding possui um problema importante: distribuição desigual de carga.


8. Como escolher entre Hash Sharding ou Range Sharding?

Não existe uma estratégia universalmente melhor.

Hash Sharding tende a ser interessante quando queremos distribuir uniformemente os dados e as requisições.

Por outro lado, Range Sharding pode ser interessante quando consultas por intervalo são importantes. Porém, Range Sharding exige cuidado com regiões que concentram muitos dados ou tráfego.

A decisão deve considerar principalmente como os dados serão acessados, e não apenas como eles serão armazenados.


9. Hot Partition

Uma Hot Partition ocorre quando uma determinada partição recebe uma quantidade desproporcional de tráfego ou dados.

Imagine:

Shard 1 => 10% de carga
Shard 2 => 10% de carga
Shard 3 => 80% de carga
Enter fullscreen mode Exit fullscreen mode

O sistema possui três shards, mas o terceiro está sendo muito mais utilizado, isso podemos fazer com que:

Shard 3
CPU => alta
I/O => Alta
Latência => Alta
Enter fullscreen mode Exit fullscreen mode

Enquanto os outros shards permanecem relativamente ociosos.

Esse é um problema especialmente importante porque adicionar mais shards não necessariamente resolve a situação se a estratégia de particionamento continuar direcionando a maior partes das requisições para o mesmo lugar.


10. O conceito de Rebalancing

Imagine que começos com 3 Shards.

Com o crescimento da aplicação, esses shards podem atingir seus limites e precisamos adicionar 2 novos shards, totalizando 5.

Agora surge um problema: como distribuir os dados existentes entre os novos shards?

Esse processo é chamado de Rebalancing, o sistema precisa mover ou redistribuir os dados par que a carga fique equilibrada corretamente.

Por exemplo:

imagem6

O objetivo é evitar que alguns shards fiquem sobrecarregados enquanto outros possuem capacidade sobrando.

Mas porque o Rebalacing é difícil?

Mover dados em grande escala não é uma operação gratuita, imagine um sistema com dezenas de terabytes armazenados, se precisarmos redistribuir esses dados, temos:

  • Transferência de grandes volumes de dados;
  • Consumo de I/O intensivo;
  • Utilização de rede;
  • Processamento adicional;
  • Possível impacto nas consultas;

Por isso, uma estratégia de particionamento precisa considerar não apenas como distribuir os dados hoje, mas também como o sistema crescerá no futuro.


11. O problema das consultas entre Shards

Agora imagine que temos:

Shard 1 => Users A-M
Shard 2 => Users N-Z
Enter fullscreen mode Exit fullscreen mode

Uma consulta simples pode ser direcionada diretamente para um Shard:

imagem8

Mas imagine uma consulta que precisa analisar todos os usuários:

SELECT COUNT(*)
FROM Users
WHERE age > 30
Enter fullscreen mode Exit fullscreen mode

Agora os dados podem estar distribuídos entre 5 Shards diferentes.

O sistema pode precisar consultar múltiplos shards e combinar os resultado, porque todos os usuários tem a idade registrada. Isso aumenta a complexidade e pode aumentar a latência.

Isso é um dos principais tradeoffs do Sharding: distribuir os dados aumenta a capacidade do sistema, mas torna determinadas operações mais complexas.


12. Cross-Shard Operations

O problema fica ainda mais evidente quando uma operação precisa modificar dados localizados entre diferentes Shards.

Imagine:

Order => Shard 1
Payment => Shard 2
Enter fullscreen mode Exit fullscreen mode

Agora uma operação precisa modificar ambos, em um banco centralizado, isso pode ser relativamente simples utilizando uma transação atômica.

Em um sistema particionado, temos uma operação distribuída entre diferentes nós, isso introduz problemas de coordenação e consistência.

Não vamos aprofundar em transações distribuídas ainda, porque elas fazem mais sentido depois que entendermos completamente os mecanismos de distribuição e consistência.

Mas o importante nesse momento é entender que: Quanto mais distribuímos os dados, mais cuidado precisamos ter com operações que atravessam diferentes Shards.


13. Replicação + Sharding

Agora conseguimos combinar os dois conceitos que estudamos, imagine:

imagem7

Aqui temos:

  • Sharding: divide os dados entre Shard 1 e Shard 2
  • Replicação: mantém cópias dentro de cada shard.

14. O tradeoff de Sharding

Podemos resumir os tradeoffs em uma tabela como essa:

Benefícios Custos
Escala horizontal Complexidade
Mais armazenamento Cross-shard queries
Mais escrita Hot partitions
Diminui sobrecarga Operações distribuídas

O principal ganho é permitir que o sistema ultrapasse os limites de uma única máquina.

O principal custo é que agora precisamos administrar dados distribuídos entre diferentes nós.


O que seria legal de guardar desta parte

Particionamento divide os dados entre diferentes partes, enquanto Sharding distribui essas partes entre diferentes nós.

Para determinar onde cada dado será armazenado, utilizamos uma Partition Key, que precisa ser escolhida considerando tanto a distribuição dos dados quanto a distribuição da carga.

Hash Sharding tende a favorecer uma distribuição mais uniforme, enquanto Range Sharding organiza os dados por intervalos e pode beneficiar determinados padrões de consulta. Uma escolha inadequada pode produzir Hot Partitions, onde uma parte do sistema recebe uma carga excessiva.

Conforme o sistema cresce, pode ser necessário realizar Rebalacing, redistribuindo os dados entre novos Shards.

O grande benefício do Sharding é permitir a escalabilidade horizontal de armazenamento e processamento, enquanto os principais custos são a complexidade, consultas entre shards, rebalancing e operações que precisam envolver múltiplas partições ao mesmo tempo.

Top comments (0)