Sharding and Partitioning Strategies in System Design: Range, Hash, and Directory Explained

  • Last Updated: August 7, 2026
  • By: javahandson
  • Series
img

Sharding and Partitioning Strategies in System Design: Range, Hash, and Directory Explained

Sharding and partitioning strategies in system design explained simply — compare range, hash, and directory sharding, pick the right shard key, and avoid hotspots.

1. Introduction

Your database was fast on day one. Then your users grew, your tables crossed a few hundred million rows, and every query started to crawl. You added an index. You upgraded the server. It helped for a while, and then it stopped helping. This is the moment most backend teams meet database sharding for the first time.

Sharding and partitioning are the tools we reach for when one database can no longer hold all the data or serve all the traffic. Instead of one giant table on one giant machine, we split the data into smaller pieces and spread them across many machines. Each piece is easier to store, faster to query, and simpler to scale. The tricky part is deciding how to split the data, and that is exactly what this article is about.

This guide is written for backend developers who already build services but have not yet designed a data layer that spans many machines. We will keep the language simple and the examples practical. We will cover the three classic partitioning strategies — range, hash, and directory — and walk through when each one shines and where each one hurts. By the end, you will be able to pick a shard key and a strategy with confidence, and explain your choice clearly in a system design interview.

A quick note on scope before we start. Sharding is a big topic, so we will not cover every corner. Instead, we focus on the ideas you meet most often on the job and in interviews: how to split data, how to route to the right piece, and what breaks once your data outgrows one machine. Get these right, and the deeper topics become far easier to pick up later.

2. Partitioning vs Sharding: Clearing Up the Terms

People use these two words loosely, and that causes confusion. Let us make the difference clear before we go further.

2.1 What Is Partitioning?

Partitioning means splitting one large table into smaller parts called partitions. The idea is to break a big dataset into chunks that are easier to manage. Partitioning can happen inside a single database, on a single machine. Many databases support this out of the box.

There are two directions you can split a table. You can split by rows or by columns, and the two have different names.

  • Horizontal partitioning: you split by rows. Each partition holds a subset of the rows but keeps all the columns. For example, users 1 to one million go in one partition, and users one million to two million go in another.
  • Vertical partitioning: you split by columns. Each partition holds a subset of the columns. For example, a user’s login details sit in one table and their profile bio and photo sit in another.

Most system design conversations are about horizontal partitioning, because that is what lets you handle huge row counts. So when we talk about splitting data across machines below, we mean the horizontal kind.

2.2 What Is Sharding?

Sharding is horizontal partitioning taken across many machines. Each partition now lives on its own database server, and each such server is called a shard. So a shard is just a partition that sits on a separate node.

Here is the simple way to remember it. Partitioning is about how you split the data. Sharding is about where the split pieces live. When the partitions move to different servers, we call it sharding. The strategies we discuss next — range, hash, and directory — apply to both, but they matter most once the data is spread across shards.

Aspect Partitioning Sharding
Scope Within one database Across many databases
Main goal Easier data management Scale beyond one machine
Machines Usually a single node Many nodes
Failure impact One database down Only one shard affected
Complexity Lower Higher

3. Why Do We Shard in the First Place?

Sharding adds real complexity, so we should only do it when we have a good reason. There are three common reasons, and usually more than one applies at the same time.

  • Storage limits. A single machine can only hold so much data. When your dataset grows past what one disk or one server can store, you must spread it out.
  • Throughput limits. One database can only handle so many reads and writes per second. Splitting the load across shards lets each shard handle a slice of the traffic.
  • Latency limits. Smaller tables mean smaller indexes and faster queries. A query that scans a billion rows is slow. The same query over a hundred million rows on a shard is far quicker.

Notice that sharding is a last resort, not a first move. Before you shard, try a read replica, a cache, or a bigger machine. Sharding is powerful, but it makes joins, transactions, and reporting harder. Reach for it only when simpler options run out.

4. The Shard Key: The Most Important Decision

Before we compare strategies, we need to understand the shard key. The shard key is the column, or set of columns, that decides which shard a row goes to. Every strategy takes the shard key as its input and returns a shard as its output.

The shard key choice shapes everything. A good key spreads data and traffic evenly across shards. A bad key piles most of the load onto one shard, which is called a hotspot. When you have a hotspot, one shard melts while the others sit idle, and you get none of the benefit of sharding.

4.1 What Makes a Good Shard Key?

  • High cardinality: the key should have many distinct values, so the data can spread widely. A boolean flag is a terrible key. A user ID is a good one.
  • Even distribution: values should be spread evenly, with no single value that dominates. If half your rows share one value, that value becomes a hotspot.
  • Matches your queries: the key should appear in most of your queries. If you shard by user ID but always search by email, every query must check every shard.

Keep these three rules in mind as we go through each strategy, because the strategy and the key work together. A great strategy with a poor key still gives you a broken system.

5. The Three Partitioning Strategies at a Glance

There are three classic ways to map a shard key to a shard: range-based, hash-based, and directory-based. Each takes the same input, the shard key, and answers the same question — which shard? — but each answers it differently. The diagram below shows all three side by side before we dig into each one.

 range-based, hash-based, and directory-based sharding

6. Range-Based Sharding

Range-based sharding splits data using ranges of the shard key. You pick boundaries, and each shard owns one continuous range. Think of it like a set of encyclopedias, where volume one holds A to F and volume two holds G to L.

6.1 How It Works

Say you shard users by their user ID. You might place IDs 1 to one million on shard A, IDs one million to two million on shard B, and so on. To find a row, you check which range its key falls into and go straight to that shard. The lookup is simple and needs no extra table.

The same idea works well for time-based data. Logs from January live on one shard, February on the next. This is common for event data, order history, and any system where records arrive in time order.

# Range-based routing (language-neutral pseudocode)
 
function pickShard(userId):
    if userId <= 1_000_000:
        return "shard_A"
    else if userId <= 2_000_000:
        return "shard_B"
    else:
        return "shard_C"
 
# A read or write first resolves the shard,
# then runs the query on that shard only.

6.2 Strengths

  • Range queries are efficient. If you ask for users 1 to 5000, they all sit on one shard, so you touch just one machine.
  • The routing logic is simple and easy to reason about. No lookup table is needed.
  • Adding a new shard for a new range is straightforward, for example a fresh shard for next year’s data.

6.3 Weaknesses

  • Hotspots are the big danger. If new users always get the highest IDs, then every new signup writes to the last shard. That shard runs hot while the older shards stay quiet.
  • Uneven ranges cause imbalance. If one range is far more active than others, its shard carries too much load.
  • Time-based ranges often make the newest shard the busiest, since recent data is usually accessed most.
Interview Insight
Interviewers love to probe hotspots. If you say range-based sharding, expect the follow-up: what happens when all new writes land on the newest shard?
A strong answer names the risk out loud and offers a fix — for example, choosing a key that does not grow monotonically, or combining ranges with a hashing step so hot ranges get spread out.

7. Hash-Based Sharding

Hash-based sharding runs the shard key through a hash function and uses the result to pick a shard. The hash scrambles the key, so even keys that look similar land on different shards. This is the most popular strategy for spreading load evenly.

7.1 How It Works

The classic formula is hash(key) % N, where N is the number of shards. You hash the key to get a big number, then take the remainder when you divide by N. That remainder is the shard number. Because a good hash spreads values evenly, the rows spread evenly too.

# Hash-based routing (language-neutral pseudocode)
 
function pickShard(key, shardCount):
    h = hash(key)          # e.g. a stable 64-bit hash
    index = h % shardCount # remainder gives the shard number
    return shards[index]
 
# Same key always hashes to the same shard,
# so reads and writes stay consistent.

7.2 Strengths

  • Load is spread evenly. A good hash avoids hotspots because it scatters keys across all shards.
  • Routing is fast and needs no lookup table. You just compute the hash and the remainder.
  • It works well for point lookups by key, which cover most read and write traffic in typical apps.

7.3 Weaknesses

  • Range queries become expensive. Since the hash scatters nearby keys everywhere, a query for users 1 to 5000 must ask every shard and merge the results.
  • Resharding is painful with plain modulo. If you change N from 4 to 5, almost every key now maps to a different shard, so you must move most of your data.

7.4 Consistent Hashing: A Better Modulo

The resharding pain has a well-known fix called consistent hashing. Instead of hash(key) % N, you place both shards and keys on a virtual ring. Each key belongs to the next shard clockwise on the ring.

The win is this: when you add or remove a shard, only the keys near that shard on the ring need to move. The rest stay put. This makes scaling far smoother, which is why systems like Cassandra and DynamoDB lean on consistent hashing under the hood. It is a deeper topic on its own, but knowing the name and the reason is often enough to impress in an interview.

Interview Insight
When you mention hash-based sharding, the natural next question is: how do you add a shard without moving all your data?
The magic words are consistent hashing. Explain that plain modulo remaps almost everything, while consistent hashing moves only a small slice of keys. That single term shows you have thought past the textbook version.

8. Directory-Based Sharding

Directory-based sharding keeps a lookup table that maps each key, or each group of keys, to a shard. Instead of computing the shard with a formula, you look it up. This is the most flexible strategy, and also the one with the most moving parts.

8.1 How It Works

You maintain a small, fast service — the directory — that answers one question: given this key, which shard holds it? When a request comes in, you first ask the directory, then go to the shard it names. The mapping lives in the directory, not in your code, so you can change it any time.

# Directory-based routing (language-neutral pseudocode)
 
# The directory is a fast key -> shard map,
# often cached in memory or held in Redis.
 
function pickShard(key):
    shard = directory.lookup(key)   # e.g. "user_42" -> "shard_B"
    if shard is null:
        shard = assignNewShard(key) # place new keys deliberately
        directory.put(key, shard)
    return shard

8.2 Strengths

  • Maximum flexibility. You can move a single tenant or user to a different shard just by updating the directory.
  • You can balance load by hand. If one customer grows huge, give them their own shard and note it in the directory.
  • Adding shards is easy. Point new keys at the new shard in the directory, with no formula to change.

8.3 Weaknesses

  • The directory is a single point of failure. If it goes down, no one can find their data. You must make it highly available and usually cache it heavily.
  • Every request pays a lookup cost. You add one hop before reaching the data. Caching the directory reduces this, but the dependency remains.
  • The directory itself can become a bottleneck at very high traffic, so it needs its own scaling and caching plan.

Directory-based sharding fits multi-tenant systems well. Think of a SaaS product where each customer is a tenant. Mapping tenant to shard in a directory lets you isolate big customers and move tenants around as they grow.

9. Comparing the Three Strategies

Now that we have seen each one, let us put them next to each other. No strategy wins every time. The right choice depends on your queries, your growth, and how much complexity you can handle.

Factor Range Hash Directory
Even load Weak Strong Manual
Range queries Excellent Poor Depends
Point lookups Good Excellent Good
Resharding Moderate Hard (plain) Easy
Extra lookup None None Yes
Complexity Low Medium High
Best for Time data Even spread Multi-tenant

9.1 A Simple Way to Choose

  • Pick range when your queries scan continuous ranges, like time-series logs or order history by date.
  • Pick hash when you mostly do point lookups by key and you want load spread evenly with the least fuss.
  • Pick directory when you need fine control, such as isolating large tenants in a multi-tenant SaaS product.

10. Common Challenges After You Shard

Sharding solves scale, but it creates new problems. Knowing these ahead of time saves you from painful surprises in production.

10.1 Cross-Shard Joins and Queries

Once data lives on many shards, a join across shards is slow and awkward. You often must fetch from each shard and combine the results in your application. The common fix is to keep related data on the same shard, so joins stay local. Choosing the right shard key helps a lot here.

10.2 Distributed Transactions

A transaction that spans two shards is much harder than one on a single database. Full two-phase commit is slow and fragile. Many teams avoid cross-shard transactions entirely by designing so that each transaction touches only one shard. When they cannot, they lean on patterns like the saga, which trades strict consistency for practicality.

10.3 Rebalancing and Hotspots

Data grows unevenly over time, so a shard that was fine last year may be overloaded today. Rebalancing means moving data to even things out, and it is delicate work while the system is live. Consistent hashing reduces how much data moves, and a directory lets you move specific keys, but neither removes the need to watch your shards and plan for growth.

10.4 Celebrity and Hot-Key Problems

Sometimes one key gets far more traffic than any other. A celebrity user with millions of followers is the classic example. Even perfect hashing cannot help, because all that traffic targets one key on one shard. The usual answers are to cache that hot key aggressively, or to split its data further with a secondary key.

10.5 Monitoring Your Shards

You cannot fix what you cannot see. Once a system is sharded, you must watch each shard on its own, not just the whole fleet as one number. Track the size, the query rate, and the latency per shard. When one shard drifts away from the others, you have found an imbalance before it becomes an outage.

Good monitoring turns rebalancing from a panic into a plan. If you spot a shard filling up faster than the rest, you can move data early during quiet hours, instead of scrambling when it runs out of room. Treat shard metrics as a first-class dashboard.

11. Hybrid and Composite Strategies

In the real world, teams rarely use one strategy in its pure form. They mix them to get the best of each. This is worth knowing, because interviewers often push you past the textbook answer toward something practical.

11.1 Composite Shard Keys

A composite shard key uses more than one column. A common pattern is to combine a tenant ID with a user ID in a multi-tenant app. This keeps all of a tenant’s data together while still spreading load across tenants. You get locality where you need it and balance where you want it.

Another useful pattern mixes range and hash. You hash the first part of the key to avoid hotspots, then range within each bucket for ordered access. Time-series systems use this a lot. They hash by device so writes spread out, and keep time ordered inside each bucket so recent-data queries stay fast.

11.2 Two-Level Sharding

Large systems sometimes shard in two layers. The first layer decides which cluster of shards holds the data. The second layer decides which shard inside that cluster. This sounds complex, and it is, but it gives you room to grow without a giant rebalance every time you add capacity. You add a whole cluster at once rather than reshuffling every key.

The lesson here is simple. Do not feel locked into one pure strategy. The three basic strategies are your building blocks, and real systems combine them freely to match how data is written and read.

12. A Worked Example: Sharding a Chat Application

Theory sticks better with a concrete example. Let us walk through sharding a chat application, the kind where users send messages inside conversations. This is a good teacher because it has clear read and write patterns, and the right shard key is not obvious at first.

12.1 Understanding the Access Pattern

First, we look at how the data is used, because that drives every choice. In a chat app, most reads fetch the recent messages of one conversation. Most writes add a new message to a conversation. Users almost always work inside one conversation at a time, so queries rarely span many conversations at once.

That single observation points us straight at the shard key. If we shard by conversation ID, then all messages of one chat land on the same shard. A read for a conversation touches exactly one shard. A write goes to one shard too. This is the locality we want.

12.2 Choosing the Strategy

Now which strategy fits? Range-based on conversation ID risks hotspots, since new and busy chats could cluster together. Hash-based on conversation ID spreads chats evenly across shards and keeps each conversation whole on one shard. For most chat apps, hash-based sharding on conversation ID is the clean choice.

# Chat app: shard by conversation, not by message.
 
function pickShard(conversationId, shardCount):
    h = hash(conversationId)
    return shards[h % shardCount]
 
# All messages of one conversation share a shard,
# so reading a chat's history hits a single shard.

12.3 Handling the Hard Cases

No design is free of trouble. A giant group chat with millions of messages can overload its shard. That is a hot-key problem, and we handle it by splitting that one conversation across shards by time, or by caching its recent messages hard. A user’s list of all conversations is another cross-shard query, which we solve with a separate index that maps each user to their conversations. These fixes are normal. Good sharding is not about avoiding every problem; it is about knowing which problems you are choosing.

13. Sharding in Practice: Tools and Databases

You rarely write raw shard-routing code by hand in a real project. Instead, you lean on tools and databases that handle much of it for you. Here is the practical landscape.

  • Vitess: a mature sharding layer for MySQL, born at YouTube. It hides shard routing behind a normal SQL interface, so your application talks to it like one database.
  • Apache ShardingSphere: a popular open-source option. It plugs in as a database driver or a proxy and handles sharding rules for you.
  • Cassandra and DynamoDB: these NoSQL stores shard automatically using consistent hashing. You pick a partition key and they spread the data for you.
  • MongoDB: supports range, hash, and zone-based sharding natively. You choose the shard key and the strategy at the collection level.
  • PostgreSQL with Citus: turns a single database into a distributed one, spreading tables across worker nodes by a chosen distribution column.

The lesson is clear. Understand the strategies deeply, but let proven tools do the heavy routing. Your real job is picking the right shard key and the right strategy for your access patterns. That decision is yours to make, and no tool can make it for you.

14. Interview Insights and Key Takeaways

Sharding shows up in almost every senior system design interview. Interviewers want to see that you understand the trade-offs, not just the definitions. Here is how to stand out.

  • Start with the shard key. Before naming a strategy, state your shard key and why it spreads load evenly. This shows mature thinking.
  • Name the hotspot risk. For any strategy, explain where the load could pile up and how you would prevent it.
  • Know consistent hashing. Mentioning it at the right moment signals real depth beyond the basics.
  • Respect the cost. Say clearly that sharding is a last resort after caching and replicas, and that it makes joins and transactions harder.

Q: What are the main sharding strategies?

A: The three main sharding strategies are range-based, hash-based, and directory-based. Range-based sharding splits keys into continuous ranges and suits time-ordered data. Hash-based sharding runs keys through a hash function to spread load evenly, which is best for point lookups. Directory-based sharding keeps a lookup table that maps each key to a shard, giving the most flexibility for multi-tenant systems.

Q: Which sharding strategy is best?

A: No single strategy is best for every case. Pick range when your queries scan continuous ranges like logs or order history. Pick hash when you mostly do point lookups and want load spread evenly. Pick directory when you need fine control, such as isolating large tenants in a SaaS product. The right choice depends on your access pattern and growth.

Q: How do I choose a shard key?

A: A good shard key has high cardinality, spreads values evenly, and appears in most of your queries. High cardinality means many distinct values, so data can spread widely. Even distribution avoids hotspots where one shard carries most of the load. Matching your queries means most reads and writes hit one shard instead of every shard.

Q: What is the difference between partitioning and sharding?

A: Partitioning splits one large table into smaller parts, usually within a single database. Sharding takes that split across many machines, where each partition lives on its own server called a shard. In short, partitioning is about how you split the data, and sharding is about where the split pieces live.

Q: Why is consistent hashing better than modulo sharding?

A: Plain modulo sharding remaps almost every key when you add or remove a shard, forcing a huge data move. Consistent hashing places shards and keys on a virtual ring, so adding or removing a shard moves only a small slice of keys. This makes scaling far smoother, which is why systems like Cassandra and DynamoDB rely on it.

Interview Insight
A classic prompt is: design a URL shortener or a social feed at scale. The moment data outgrows one machine, sharding enters the picture.
Walk through it in order: pick the shard key, choose a strategy and justify it against the query pattern, then flag the challenges — cross-shard queries, rebalancing, and hot keys. That structure alone marks you as a strong candidate.

15. Conclusion

Sharding is how systems grow past the limits of a single machine. You split the data horizontally, spread the pieces across shards, and use a strategy to route each key to its home. The three classic strategies each fit a different need. Range-based sharding suits continuous and time-ordered queries. Hash-based sharding spreads load evenly for point lookups. Directory-based sharding gives you fine control for multi-tenant systems.

The decision that matters most is the shard key, because it decides whether your load spreads or piles up. Choose a key with high cardinality that matches your queries, then pick the strategy that fits how you read and write. Keep the challenges in mind — cross-shard joins, distributed transactions, rebalancing, and hot keys — and design to avoid them where you can.

Master these ideas, and you will not only build systems that scale, you will also explain your choices with confidence when it counts. In the next articles of this series, we will build on this foundation as we move deeper into distributed data and the patterns that keep it consistent.

Further Reading

To go deeper into these topics, explore the following trusted sources:

  • MongoDB Manual — Sharding: https://www.mongodb.com/docs/manual/sharding/
  • Apache ShardingSphere Documentation: https://shardingsphere.apache.org/document/current/en/overview/
  • Vitess — Sharding Documentation: https://vitess.io/docs/concepts/shard/
  • Amazon DynamoDB — Partitions and Data Distribution: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.Partitions.html
  • Designing Data-Intensive Applications by Martin Kleppmann — Chapter 6, Partitioning: https://dataintensive.net/

Leave a Comment