Israel Nunes

Last updated: November 7th, 2026Atualizado em 7 de novembro de 2026

← Back to blog← Voltar para o blog

System Design Basics: Database ShardingFundamentos de System Design: Sharding de Banco de Dados

March 21, 202621 de março de 2026·7-8 min read7-8 min de leitura
basicssystem designshardingprogramming

Hello, and thank you for joining me again! I am starting a series of posts about System Design Basics! The idea is to write about different topics that, together, can give us a broader vision on system design.

It is important to mention that this series is highly inspired by Hello Interview - Core Concepts articles! Give them a look, it is more than worth it!

For this post (#2), we are focusing on Sharding!

What is database sharding?

When an application starts growing, consequently, the amount of data it needs to store will increase over time. As data volume becomes too large and the application has to deal with too many reads and writes, the database becomes a bottleneck, affecting customer experience and slowing down the whole operation.

No problem! Just upgrade to a larger database instance with more processing and storing capacity... The problem is that this only works for a short while.

Eventually the application hits the ceiling of what a single machine can handle. Then, when vertical scaling and other optimizations are no longer enough, we need to move from scaling up to scaling out, and that is: splitting our database across multiple machines.

Database sharding is, in simple terms, the process of splitting data into smaller chunks, called shards, and storing them across several database servers.

What are the benefits?

Improve response time: As we split our monolithic database into smaller sets of data, each shard has fewer rows than the entire database, hence, it takes less time to retrieve specific information, or run a query. (given a scenario where queries can be routed to a single shard)

Availability: Distributing parts of the database across multiple machines prevents a single point of failure from causing a total system outage. It means that downtime doesn't take the whole application. Important to mention that sharding is often done along with data replication, so, in this case, when one shard becomes unavailable, that specific data can be accessed and restored from another shard.

Scaling: Sharding allows an application to scale horizontally as data volume increases, without requiring significant downtime (that is why choosing the correct shard key is really important - we'll get there).

How to choose a shard key?

First of all, a shard key is the field or column we use to split the dataset that, consequently, defines how data is grouped.

That said, the process of selecting a good/optimal shard key is crucial for the application. A poor decision leads to consequences such as uneven data distribution, hot spots and distributed queries (slow operations) to gather the necessary information.

But then, how to choose a good shard key?

An optimal key should have a high cardinality, even distribution, and should align with the application's most common access patterns. (some teams use Compound Shard Keys, one column isn't enough to guarantee high cardinality, so engineers combine two fields)

It means that the chosen key should have (or be able to have) many unique values. Imagine sharding by a boolean field, for the lifetime of the application, we'll only have two shards. On the other hand, if we choose to shard by user ID, it now gives us enough room to scale and distribute our data.

In addition, the content (values) should spread evenly across shards, which means consistent distribution in order to avoid hot spots! If you choose to shard based on user birth date, but your application is focused on gen X, then, you might find yourself in trouble.

Methods of database sharding

Now that we have chosen our shard key, we must then decide on how to distribute that data across shards. That said, let's have a quick look at some sharding strategies.

Range-based

Range sharding is one of the simplest strategies. It groups values based on pre-configured set of ranges. We can choose a shard key like user_id, then assign value ranges to shards.

Based on the example above, we could allocate users_id in range of 1 to 1,000 at Server A and 1,001 to 2,000 at Server B, and so on.

Although pretty straightforward and simple, it could lead to hot spots depending on the shard key we've chosen. For example, if we choose our shard key as created_at, and we allocate all new users to Server B, this server will do all the job while Server A could sit idle.

Geo-based

In this strategy, our database sharding takes into consideration the geographical server distribution based on users' physical location. In simpler terms, the data from users in Ireland will be allocated to servers based in EU, on the other hand, users based in Brazil have their servers in South America.

This method is one way of reducing application latency for a better user experience and help companies address compliance requirements given geographical locations. One great example is a users table sharded by region (EU, US, LATAM).

Hash-based

Hash sharding is a method based on a mathematical function (hash function) to evenly distribute records across shards. As mentioned earlier, if we pick user_id as our shard key, instead of distributing based on ranges, we get the value of the ID, hash it, and use the result to pick a shard.

Consistency and even distribution — this is one of the main advantages of using a hash function to map out the distribution. On the other hand, we add complexity when we need to add or remove shards (servers). This is because the hash function uses a modulo that references the number of shards (servers) currently available to distribute records, and, whenever this number changes, we must repartition our data and move massive amounts of data around.

To address this, consistent hashing places both the data and the servers on a conceptual 'ring,' meaning only a small fraction of data needs to move when a server is added or removed. This is a fascinating topic on its own, and I'll be dedicating an entire future post to deep-diving into how it works!

Directory-based

In this method we set a lookup table to decide where each record lives. We decouple the shard key from physical placement by attributing a key (shard key) to values (shards).

Advantages: flexibility — if some user generates huge traffic you can move them to a dedicated shard. When we need to rebalance, just update the lookup table. Downsides: every single request requires a lookup, and this adds latency to our reads or writes. Also, if the directory where our map table lives goes down, our entire system stops, even if our data shards are healthy.

What are the Challenges?

As we've seen, sharding is a powerful tool to scale our database, but it is not a silver bullet. Distributing data across multiple servers adds a significant layer of complexity to the system. Let's look at some of the main challenges we might face.

Hot Spots and load imbalance

We briefly touched on this when discussing how to choose a shard key, but it remains one of the most common headaches. A hot spot happens when one specific shard receives a disproportionate amount of traffic or data compared to the others. Even if the data is distributed evenly, certain records or users might suddenly generate massive traffic. When one server takes all the heavy lifting while the others sit idle, it becomes a bottleneck, defeating the original purpose of scaling out.

Cross-shard operations

In a single database, running a query that joins multiple tables is standard practice. However, when our data is partitioned across several machines, running those same queries becomes a massive challenge. If an operation needs to fetch or join data that lives in different shards, the system has to query each server individually, pull the data over the network, and stitch it back together. These distributed queries are notoriously slow and expensive, directly impacting the response times we were trying to improve.

Maintaining consistency

In a monolithic setup, we rely on the database to handle transactions safely. If an operation fails halfway through, the database rolls everything back, maintaining data consistency. In a sharded environment, a single transaction might need to update data on two or more separate servers at the same time. If the update succeeds on Server A but fails on Server B, our data is now inconsistent. Fixing this requires implementing distributed transactions, which adds a lot of complexity and overhead to our architecture.

References

Hello Interview - Sharding

AWS - What is Database Sharding?

Google Cloud - What is Database Sharding

Olá, e obrigado por estar aqui de novo! Estou começando uma série de posts sobre Fundamentos de System Design! A ideia é escrever sobre temas diferentes que, juntos, nos deem uma visão mais ampla de system design.

Vale dizer que esta série é bastante inspirada nos artigos de Core Concepts do Hello Interview! Dê uma olhada, vale muito a pena!

Neste post (#2), o foco é sharding!

O que é sharding de banco de dados?

Quando uma aplicação começa a crescer, a quantidade de dados que ela precisa guardar aumenta com o tempo. Quando o volume de dados fica grande demais e a aplicação precisa lidar com leituras e escritas demais, o banco de dados vira um gargalo, prejudicando a experiência do cliente e deixando toda a operação mais lenta.

Sem problema! É só migrar para uma instância de banco maior, com mais capacidade de processamento e armazenamento... O problema é que isso só resolve por um tempo.

Uma hora a aplicação bate no teto do que uma única máquina aguenta. Então, quando o escalonamento vertical e outras otimizações já não bastam, precisamos passar de escalar para cima para escalar para os lados, ou seja: dividir o banco de dados entre várias máquinas.

Sharding de banco de dados é, em termos simples, o processo de dividir os dados em pedaços menores, chamados shards, e guardá-los em vários servidores de banco de dados.

Quais são os benefícios?

Melhor tempo de resposta: ao dividir o banco monolítico em conjuntos menores de dados, cada shard tem menos linhas do que o banco inteiro e, por isso, leva menos tempo para recuperar uma informação específica ou rodar uma consulta. (num cenário em que as consultas possam ser direcionadas a um único shard)

Disponibilidade: distribuir partes do banco entre várias máquinas evita que um ponto único de falha derrube o sistema inteiro. Ou seja, uma queda não leva a aplicação toda junto. Vale dizer que o sharding costuma vir acompanhado de replicação de dados; assim, quando um shard fica indisponível, aquele dado pode ser acessado e restaurado a partir de outro shard.

Escalabilidade: o sharding permite que a aplicação escale horizontalmente à medida que o volume de dados cresce, sem exigir longos períodos fora do ar (é por isso que escolher a shard key certa é tão importante — já chegamos lá).

Como escolher uma shard key?

Antes de tudo, a shard key é o campo ou a coluna que usamos para dividir o conjunto de dados e que, por consequência, define como os dados são agrupados.

Dito isso, escolher uma shard key boa (ou ótima) é crucial para a aplicação. Uma escolha ruim leva a consequências como distribuição desigual dos dados, hot spots e consultas distribuídas (operações lentas) para juntar a informação necessária.

Mas então, como escolher uma boa shard key?

Uma chave ideal deve ter alta cardinalidade, distribuição uniforme e estar alinhada aos padrões de acesso mais comuns da aplicação. (alguns times usam shard keys compostas: quando uma coluna só não garante alta cardinalidade, os engenheiros combinam dois campos)

Isso quer dizer que a chave escolhida deve ter (ou poder ter) muitos valores únicos. Imagine fazer sharding por um campo booleano: durante toda a vida da aplicação, teremos só dois shards. Já se escolhermos o ID do usuário, temos espaço de sobra para escalar e distribuir os dados.

Além disso, o conteúdo (os valores) deve se espalhar de forma uniforme entre os shards, ou seja, uma distribuição consistente para evitar hot spots! Se você escolher fazer sharding pela data de nascimento do usuário, mas a sua aplicação é focada na geração X, pode acabar com problemas.

Métodos de sharding

Agora que escolhemos a shard key, precisamos decidir como distribuir os dados entre os shards. Vamos dar uma olhada rápida em algumas estratégias de sharding.

Por faixa (range-based)

O sharding por faixa é uma das estratégias mais simples. Ele agrupa os valores com base num conjunto de faixas pré-configuradas. Podemos escolher uma shard key como user_id e atribuir faixas de valores a cada shard.

Seguindo o exemplo acima, poderíamos alocar os user_id de 1 a 1.000 no Servidor A, de 1.001 a 2.000 no Servidor B, e assim por diante.

Apesar de bem direto e simples, isso pode gerar hot spots, dependendo da shard key escolhida. Por exemplo, se escolhermos created_at como shard key e alocarmos todos os usuários novos no Servidor B, esse servidor faz todo o trabalho enquanto o Servidor A fica ocioso.

Por geografia (geo-based)

Nesta estratégia, o sharding leva em conta a distribuição geográfica dos servidores com base na localização física dos usuários. Em termos simples, os dados de usuários na Irlanda ficam em servidores na Europa, enquanto os usuários no Brasil têm seus servidores na América do Sul.

Esse método é uma forma de reduzir a latência da aplicação para uma experiência melhor e ajuda as empresas a atender a requisitos de compliance de cada região. Um ótimo exemplo é uma tabela de usuários dividida por região (EU, US, LATAM).

Por hash (hash-based)

O sharding por hash é um método baseado numa função matemática (função hash) para distribuir os registros de forma uniforme entre os shards. Como vimos antes, se escolhermos user_id como shard key, em vez de distribuir por faixas, pegamos o valor do ID, aplicamos o hash e usamos o resultado para escolher o shard.

Consistência e distribuição uniforme — essa é uma das principais vantagens de usar uma função hash para mapear a distribuição. Por outro lado, a complexidade aumenta quando precisamos adicionar ou remover shards (servidores). Isso porque a função hash usa um módulo que considera o número de shards (servidores) disponíveis no momento para distribuir os registros e, sempre que esse número muda, precisamos reparticionar os dados e mover uma quantidade enorme deles de lugar.

Para resolver isso, o consistent hashing coloca tanto os dados quanto os servidores num "anel" conceitual, de modo que só uma pequena fração dos dados precisa se mover quando um servidor entra ou sai. É um tema fascinante por si só, e vou dedicar um post futuro inteiro para explicar a fundo como ele funciona!

Por diretório (directory-based)

Neste método, criamos uma tabela de consulta que decide onde cada registro mora. Desacoplamos a shard key do posicionamento físico, associando uma chave (shard key) a valores (shards).

Vantagens: flexibilidade — se algum usuário gera um tráfego enorme, dá para movê-lo para um shard dedicado. Quando precisamos rebalancear, basta atualizar a tabela de consulta. Desvantagens: toda requisição exige uma consulta à tabela, o que adiciona latência às leituras e escritas. Além disso, se o diretório onde a tabela de mapeamento mora cair, o sistema inteiro para, mesmo que os shards de dados estejam saudáveis.

Quais são os desafios?

Como vimos, o sharding é uma ferramenta poderosa para escalar o banco de dados, mas não é uma bala de prata. Distribuir dados entre vários servidores adiciona uma camada significativa de complexidade ao sistema. Vamos ver alguns dos principais desafios que podemos enfrentar.

Hot spots e desbalanceamento de carga

Já tocamos nesse assunto ao falar de como escolher a shard key, mas ele continua sendo uma das dores de cabeça mais comuns. Um hot spot acontece quando um shard específico recebe uma quantidade desproporcional de tráfego ou de dados em comparação com os outros. Mesmo que os dados estejam bem distribuídos, certos registros ou usuários podem de repente gerar um tráfego enorme. Quando um servidor faz todo o trabalho pesado enquanto os outros ficam ociosos, ele vira um gargalo, e o propósito de escalar para os lados se perde.

Operações entre shards

Num único banco de dados, rodar uma consulta que junta várias tabelas é prática comum. Mas, quando os dados estão particionados em várias máquinas, rodar essas mesmas consultas vira um desafio enorme. Se uma operação precisa buscar ou juntar dados que estão em shards diferentes, o sistema tem de consultar cada servidor separadamente, trazer os dados pela rede e juntá-los de novo. Essas consultas distribuídas são notoriamente lentas e caras, afetando diretamente os tempos de resposta que queríamos melhorar.

Manter a consistência

Num ambiente monolítico, contamos com o banco de dados para lidar com as transações com segurança. Se uma operação falha no meio do caminho, o banco desfaz tudo, mantendo os dados consistentes. Num ambiente com sharding, uma única transação pode precisar atualizar dados em dois ou mais servidores ao mesmo tempo. Se a atualização der certo no Servidor A mas falhar no Servidor B, os dados ficam inconsistentes. Resolver isso exige implementar transações distribuídas, o que traz muita complexidade e custo extra para a arquitetura.

Referências

Hello Interview - Sharding

AWS - What is Database Sharding?

Google Cloud - What is Database Sharding