Demystifying Database Sharding: How to Split Data Without Splitting Your Brain

Share
Demystifying Database Sharding: How to Split Data Without Splitting Your Brain

Over the last couple of weeks, our Designing Data-Intensive Applications book club has been discussing Chapter 7: Sharding.

Unlike some of our previous sessions, these turned into less of a presentation and more of a brainstorming exercise. We would pick a design, ask "But what happens when...?", and discover that the answer usually introduced another trade-off.

There was also this exchange:

“The previous chapter sounded much bigger and lengthier than this one.”

Someone immediately replied:

“Well, that chapter was replication. This one is sharding.”

Fair point. 😄

But the joke accidentally captures the concepts rather well.

With replication, we make more copies.

With sharding, we break things apart.

And while the word sharding might sound intimidating, the basic idea is surprisingly simple.


Imagine Your Database Has Outgrown Its House

Suppose your application starts with one database server.

For a long time, life is good.

Your server stores:

Users
Orders
Products
Payments
Comments
...

Then your application grows.

One million users become ten million.

Ten million become a hundred million.

The database gets bigger. Write traffic increases. Storage fills up. CPU usage climbs.

At some point, upgrading to an even bigger machine stops being attractive—or simply stops being possible.

So instead of asking:

"How do I make this machine bigger?"

we ask:

"How do I spread the work across multiple machines?"

That is where sharding comes in.

Instead of one database containing everything:

                 Database
              All the data

we divide the dataset:

        Shard 1       Shard 2       Shard 3

       Users A-H     Users I-P     Users Q-Z

Each shard is effectively a smaller database responsible for some subset of the data. The primary motivation is scalability: data volume and write throughput can be distributed across machines rather than forcing one node to handle everything.

Sounds straightforward.

Unfortunately, distributed systems rarely let us leave the meeting that early.

The moment we split our database, several new questions appear.

Which record goes into which shard?

How do we make sure one shard doesn't receive most of the traffic?

What happens when we add another server?

How does an application know which server contains a particular record?

What if we want to search by something other than the sharding key?

Chapter 7 is really about answering those questions.


First Rule of Sharding: Don't Shard Unless You Need To

One of my favorite lessons from this chapter isn't an algorithm.

It's restraint.

Sharding is a powerful tool, but it is also a heavyweight solution. If one machine can comfortably handle your data volume and write throughput, keeping the architecture simple is often better.

Engineers sometimes think of scalability as:

More distributed = more scalable = better architecture

A healthier equation is:

Simplest architecture that handles the required scale
                     =
              better architecture

Sharding introduces an entirely new dimension into your data model because every record now needs an answer to:

Where does this record live?

That answer is usually determined by a partition key.

And choosing that key is one of the most important decisions you'll make.


The Partition Key: Your Database's Postal Code

Think about delivering a package.

The postal service doesn't search every house in America to find your address.

Your ZIP code progressively narrows down where the package needs to go.

A partition key does something similar.

Suppose we have:

user_id = 12345

Our sharding algorithm might determine:

user_id 12345 → Shard 7

Now the database knows exactly where to look.

But imagine querying something that doesn't tell us the partition key:

Find everyone named "John"

Suddenly we may have no idea which shard contains those users.

We might have to ask:

Shard 1: Any Johns?
Shard 2: Any Johns?
Shard 3: Any Johns?
...
Shard 100: Any Johns?

You've just turned one query into 100 queries.

This is why choosing the partition key is not merely a storage decision.

It can shape your application's entire query model.


Strategy 1: Sharding by Key Range

The easiest strategy to visualize is key-range sharding.

Imagine an encyclopedia.

Volume 1 contains:

A–B

Volume 2:

C–D

and so on.

To look up Database, you don't search every volume. You know roughly which one should contain it.

Databases can do the same thing:

Shard 1: customer_id 1       – 1,000,000
Shard 2: customer_id 1,000,001 – 2,000,000
Shard 3: customer_id 2,000,001 – 3,000,000

The book uses essentially this encyclopedia analogy when introducing key-range sharding.

This approach has a wonderful property:

Range queries are easy.

Suppose you're storing sensor readings:

sensor_42:2026-09-01
sensor_42:2026-09-02
sensor_42:2026-09-03

Because related keys remain together and sorted, asking for:

"Give me sensor 42's readings from September 1–30"

can be very efficient. The chapter specifically highlights sorted storage and efficient range scans as an advantage of key-range sharding.

But there is a trap.


The Celebrity Problem: Hot Shards

Imagine partitioning events by timestamp.

August data    → Shard 8
September data → Shard 9
October data   → Shard 10

Looks logical.

Except it's currently September.

Where are almost all new writes going?

                    ┌─────────────┐
August shard        │             │  😴
                    └─────────────┘

                    ┌─────────────┐
September shard     │🔥🔥🔥🔥🔥🔥 │
                    └─────────────┘

                    ┌─────────────┐
October shard       │             │  😴
                    └─────────────┘

Congratulations: you have ten machines and the performance characteristics of approximately one machine.

This is called a hot shard or hot spot.

A related problem is a hot key.

Imagine a social network where most users receive a handful of interactions.

Then Taylor Swift posts something.

Millions of users suddenly interact with the same user or post ID.

Your hashing algorithm may be distributing keys perfectly evenly, but the traffic itself isn't evenly distributed. The chapter makes exactly this distinction: uniform key distribution does not guarantee uniform load when a few keys receive disproportionate traffic.

That's an important distributed-systems lesson:

Balanced data does not necessarily mean balanced work.

Strategy 2: Hash the Key

One way to avoid neighboring keys piling into the same shard is to hash them.

Suppose our IDs are:

1001
1002
1003
1004

Instead of placing nearby values together, a hash function might produce something conceptually like:

hash(1001) → 847263
hash(1002) → 193724
hash(1003) → 638291
hash(1004) → 372918

Nearby input values no longer produce nearby output values.

The data gets scattered.

That is great for distributing load.

But we've paid for that benefit.

Remember our nice range query?

Find IDs 1000–2000

Those records may now exist across many shards.

Hashing improves distribution by deliberately destroying ordering; as the chapter's summary notes, this generally makes range queries less efficient.

This trade-off comes up constantly in system design:

Key-range sharding
        ↓
Great locality and range queries
        ↓
Higher hot-spot risk


Hash sharding
        ↓
Better distribution
        ↓
Poor key ordering / range queries

Neither is "the correct algorithm."

Your access patterns decide.


Why hash(key) % number_of_servers Is a Trap

At first, hashing seems to give us an obvious implementation.

Suppose there are four servers:

server = hash(key) % 4

Beautiful.

Until Black Friday arrives and you need a fifth server.

Now:

server = hash(key) % 5

The mapping changes for a huge number of keys.

Records that previously belonged to Server 2 might now belong to Server 4.

Records on Server 1 might move to Server 3.

Adding one machine can trigger massive amounts of data movement.

Chapter 7 explicitly calls out this weakness of the simple mod N approach: changing N remaps many keys unnecessarily.

So databases use smarter strategies.

One is to create many more shards than physical machines.

For example:

1,000 logical shards
10 database nodes
≈ 100 shards/node

When Node 11 arrives, instead of recalculating where every key belongs, we move some whole shards:

Node 1 ──┐
Node 2 ──┼──> Node 11
Node 3 ──┘

The mapping:

key → shard

doesn't change.

Only:

shard → server

changes.

That's much easier to manage.


Consistent Hashing: "Please Move as Little as Possible"

This leads us to one of those terms engineers hear frequently:

consistent hashing.

The name can be slightly misleading. "Consistent" here is not about database consistency guarantees.

The useful mental model is:

When the cluster changes, try to keep each key where it already is.

A good consistent-hashing strategy aims for two things:

  1. distribute keys approximately evenly;
  2. when the number of shards changes, move as few keys as necessary.

You can think of it as reorganizing an office.

If one new employee joins, you don't want to tell all 200 employees:

"Everyone switch desks!"

You want:

"Let's move five people around and make room."

Distributed databases appreciate the same courtesy.


Rebalancing: Moving Without Stopping the World

Eventually your cluster changes.

A node is added.

A node is removed.

One shard gets too large.

A shard becomes extremely busy.

The database may need to rebalance.

With key-range sharding, for example, a big range can be split:

A ---------------- Z

becomes:

A -------- M    N -------- Z

and the two ranges can live on different nodes.

The difficulty is that splitting and moving shards is not free.

Potentially gigabytes of data need to move across the network while the database is still serving production requests.

This is why automatic rebalancing, while attractive, deserves respect.

Chapter 7 describes an especially interesting failure scenario:

  1. A node becomes overloaded.
  2. It responds slowly.
  3. Automatic failure detection decides the node must be dead.
  4. The cluster starts moving its data elsewhere.
  5. Rebalancing generates even more network and CPU load.
  6. More machines become slow.
  7. They too begin looking dead.

Welcome to a cascading failure.

Sometimes automation saves operators.

Sometimes automation is an extremely fast way to automate your outage.

There is value in occasionally keeping a human in the loop.


Okay, We Sharded the Database. Where Is My Data?

There's another problem.

Suppose:

customer_id = 42

belongs to:

Shard 17

Great.

But which machine currently hosts Shard 17?

Node A?
Node B?
Node C?
Node D?

Remember: shards move.

So clients need some mechanism for request routing.

The chapter outlines three common approaches:

1. Client → Any database node → Correct node

2. Client → Routing layer → Correct database node

3. Smart client → Correct database node directly

Approach 1 keeps clients simple but can add an extra network hop.

Approach 2 introduces a dedicated routing layer.

Approach 3 removes the middleman but makes clients smarter and requires them to keep their shard map current.

And someone must maintain the authoritative mapping:

Shard 1 → Node B
Shard 2 → Node A
Shard 3 → Node D
Shard 4 → Node B
...

Coordination systems such as ZooKeeper or etcd can fill this role; the chapter also discusses databases with built-in consensus-based coordination.

Now we have discovered something important.

Sharding isn't just:

split data across machines.

It becomes:

split data, track the pieces, move the pieces, and make sure everyone knows where the pieces currently are.

Secondary Indexes: Where Sharding Gets Really Interesting

Now imagine we're building a used-car marketplace.

We shard listings by listing_id.

Listing 127 → Shard A
Listing 742 → Shard B

Easy.

Someone searches:

"Show me listing 742."

We know exactly where to go.

But real users don't search for cars by database IDs.

They search:

Color = Red
Make = Honda
Year > 2022
Price < $30,000

These are secondary indexes.

And secondary indexes make sharding much more interesting because the thing you're searching by is no longer necessarily the thing you sharded by.

The chapter presents two major choices.


Option 1: Local Secondary Indexes

Each shard indexes only its own records.

Shard A
  red    → [12, 15, 91]
  blue   → [33, 76]

Shard B
  red    → [501, 762]
  blue   → [620]

Writing is convenient.

When record 91 changes, we update Shard A.

Done.

But ask:

"Show me every red car."

and suddenly we need:

Ask Shard A
Ask Shard B
Ask Shard C
Ask Shard D
...
Merge results

The book calls out the scalability problem clearly: if every query has to touch every shard, adding more shards doesn't necessarily increase your query throughput, and tail latency can become worse.

So:

Local indexes make writes easier but can make reads expensive.

Option 2: Global Secondary Indexes

Instead, create one logical index covering the whole dataset:

red → [12, 15, 91, 501, 762, ...]

Of course, we can't put this giant index on a single machine.

We're sharding because single machines are exactly what we're trying to escape.

So we shard the secondary index too.

Perhaps:

colors A–R → Index Shard A
colors S–Z → Index Shard B

Now searching:

color = red

can go directly to the shard containing red.

Reads improve.

Writes get harder.

Updating one car might now involve:

Primary-data shard
        +
Color-index shard
        +
Make-index shard
        +
Potentially other index shards

A single logical update has crossed several machines.

Global indexes therefore make the read path more targeted but complicate writes and consistency; some systems may update such indexes asynchronously, which can mean temporarily stale index results.

Another beautiful distributed-systems trade-off:

Local secondary index
    Cheap writes
    Expensive reads

Global secondary index
    More complicated writes
    Better targeted reads

There is no free lunch.

Only different restaurant bills.


An Underrated Use of Sharding: Multitenancy

One section I found particularly useful in the second edition is sharding for multitenant systems.

Imagine a SaaS product:

Customer A
Customer B
Customer C
Customer D

Rather than sharding arbitrary records, you might assign tenants to shards:

Shard 1 → Customers A, B, C
Shard 2 → Customers D, E, F
Shard 3 → Customer G

That can provide benefits beyond raw scalability.

The chapter identifies advantages including resource isolation, permission isolation, cell-based fault isolation, per-tenant backup and restore, data residency, and gradual schema rollout.

This gives us a broader perspective:

Sharding isn't always about "the database is too big."

Sometimes it's about creating useful boundaries.

And good boundaries are one of the recurring themes in scalable system design.

The challenge, of course, appears when one tenant becomes huge—or when features require data across several tenants. Then our neat boundary becomes another distributed-systems problem.


The Pattern Behind All of This

After discussing this chapter for two weeks, my biggest takeaway wasn't any particular hashing algorithm.

It was this:

Sharding is the art of deciding what should stay together.

Put related data together and operations within that boundary become easy.

Split data apart and you gain parallelism.

But every operation that crosses that boundary becomes more expensive.

That explains almost everything we've discussed:

Good partition key
        ↓
Most requests hit one shard
        ↓
Simple + fast


Poor partition key
        ↓
Requests span many shards
        ↓
Coordination + network calls + complexity

And it explains why some of the hardest sharding questions aren't really database questions.

They're application questions.

What data is usually accessed together?

What data is written together?

What queries must be fast?

Where could hot keys emerge?

Which operations require global knowledge?

How large could one tenant become?

Those answers should influence your sharding strategy far more than whether a particular database supports some clever hashing algorithm.


A Practical Sharding Checklist

If I were reviewing a system-design proposal involving sharding, these are the questions I'd start with:

  • Do we actually need sharding yet? Could a single database plus replication handle our scale?
  • What is the partition key? Does it align with our most common access patterns?
  • Could that key create hot spots? Think timestamps, celebrities, giant customers, popular products, and viral content.
  • Do we need range queries? Key-range sharding may preserve useful locality while hashing generally sacrifices it.
  • What happens when we add machines? Understand the rebalancing strategy and how much data must move.
  • Who knows where each shard lives? Clients, routers, database nodes, or a coordination service?
  • What queries don't contain the partition key? Those may become scatter/gather queries.
  • Do we need secondary indexes? Decide whether local or global indexing better matches your read/write workload.
  • How many operations cross shard boundaries? Frequent cross-shard work may be telling you that your partition boundary is wrong.
  • What happens during failure or rebalancing? Design for the system while it's changing, not only for its steady state.

Sharding in One Picture

If I had to summarize Chapter 7 on a whiteboard, I'd draw this:

                       DATA
                         │
                         │ Choose partition key
                         ▼
                 ┌───────────────┐
                 │ Sharding Rule │
                 └───────┬───────┘
                         │
          ┌──────────────┼──────────────┐
          ▼              ▼              ▼
       Shard A         Shard B        Shard C
          │              │              │
          ▼              ▼              ▼
       Node 1          Node 2         Node 3

                         │
                         │ Cluster changes
                         ▼
                    Rebalancing

                         │
                         │ Client asks for data
                         ▼
                 Request Routing

                         │
                         │ Search by another field?
                         ▼
                Secondary Indexes
                ┌────────┴────────┐
              Local             Global
            index                index
          easy writes         easier reads
         harder reads         harder writes

That is sharding.

The algorithms matter.

But the real engineering lies in the arrows.


Final Thought: Sharding Moves Complexity

Sharding doesn't magically make data smaller.

It changes who is responsible for which part of it.

One big problem becomes several smaller problems that can run independently.

That's the source of scalability.

But independence has a price.

Whenever an operation needs information from several shards, the complexity we removed comes right back in the form of networking, routing, coordination, distributed transactions, or result aggregation.

The chapter closes on precisely this tension: shards scale because they operate mostly independently, but writes involving several shards are inherently more difficult—especially when some succeed and others fail.

So perhaps the most useful mental model for sharding isn't:

How do I split my database?

It is:

Where can I draw boundaries so that most of my system can work independently?

Get that boundary right, and sharding becomes a powerful scaling tool.

Get it wrong, and you'll have a beautifully distributed collection of new problems.

And unlike replication, unfortunately, those problems may not come with extra copies. 😄


This article was inspired by our community discussions while reading Chapter 7, “Sharding,” of Martin Kleppmann and Chris Riccomini's second edition of Designing Data-Intensive Applications. The chapter covers sharding strategies, hot spots, rebalancing, request routing, multitenancy, and local versus global secondary indexes.


Read more

Building a Production-Grade Document Intelligence Platform: Architecture, AI Engineering, and the Tradeoffs Behind Every Decision

Building a Production-Grade Document Intelligence Platform: Architecture, AI Engineering, and the Tradeoffs Behind Every Decision

How I went from parsing messy contracts to building an evidence-grounded AI platform on Google Cloud—and why evaluating the alternatives mattered more than choosing the fanciest technology. The Problem: Documents Are Everywhere. Structured Data Isn't. Every organization deals with documents: contracts, invoices, agreements, compliance reports, and

By Vijay Bhore