🔥 0
0
Lesson 2 of 6 20 min +200 XP

Sharding Strategies

How do you decide which shard holds which data? That's where sharding strategies come in. The strategy you choose affects query performance, data distribution, and operational complexity.

There are three main approaches:

#️⃣
Hash-Based

shard = hash(key) % N

📊
Range-Based

shard by value ranges

📖
Directory-Based

lookup table mapping

Hash-Based Sharding

The most common approach. Apply a hash function to the shard key, then use modulo to determine the shard.

def get_shard(user_id, num_shards):
    # Hash the key for even distribution
    hash_value = hash(user_id)
    return hash_value % num_shards

# Examples with 4 shards
get_shard(12345, 4)  # hash(12345) % 4 = ?
get_shard(67890, 4)  # hash(67890) % 4 = ?

How It Works

User ID: 12345
         │
         ▼
   hash(12345) = 8234567891
         │
         ▼
   8234567891 % 4 = 3
         │
         ▼
      Shard 3
Pros
  • Even distribution of data
  • Simple to implement
  • Works well for point queries
Cons
  • Range queries are inefficient
  • Adding shards requires rehashing
  • No data locality

The Resharding Problem

What happens when you add a 5th shard to a 4-shard cluster?

# Before: 4 shards
user_12345: hash % 4 = 3 → Shard 3

# After: 5 shards
user_12345: hash % 5 = 1 → Shard 1  # MOVED!
The Problem

When changing from N to N+1 shards with naive modulo hashing, approximately (N-1)/N of all keys need to move. Going from 4 to 5 shards means ~80% of data must migrate. This is why we use consistent hashing (covered in lesson 4).

Range-Based Sharding

Assign data to shards based on value ranges. Common for time-series data or alphabetical ordering.

def get_shard_by_range(created_date):
    if created_date < "2023-01-01":
        return "shard_2022"
    elif created_date < "2024-01-01":
        return "shard_2023"
    else:
        return "shard_2024"

# Or by user ID ranges
def get_shard_by_id_range(user_id):
    if user_id < 1_000_000:
        return "shard_1"
    elif user_id < 2_000_000:
        return "shard_2"
    else:
        return "shard_3"

How It Works

┌─────────────────────────────────────────────────────┐
│                     User IDs                        │
├───────────────┬───────────────┬───────────────┬─────┤
│    0 - 1M     │   1M - 2M     │   2M - 3M     │ ... │
│   Shard 1     │   Shard 2     │   Shard 3     │     │
└───────────────┴───────────────┴───────────────┴─────┘

User 500,000  → Shard 1
User 1,500,000 → Shard 2
User 2,500,000 → Shard 3
Pros
  • Efficient range queries
  • Data locality for related items
  • Easy to understand and debug
Cons
  • Risk of hotspots
  • Uneven distribution possible
  • Requires careful range planning

The Hotspot Problem

Range-based sharding can create hotspots when access patterns aren't uniform:

Example: Time-Based Sharding Hotspot

If you shard orders by date and all new orders go to the "2024" shard, that shard handles ALL writes while older shards sit idle. The newest shard becomes a bottleneck.

Directory-Based Sharding

Use a separate lookup service to track which shard holds each key (or key range).

# Lookup table (could be in Redis, DB, etc.)
shard_directory = {
    "user_1": "shard_A",
    "user_2": "shard_B",
    "user_3": "shard_A",
    # ... millions of entries
}

def get_shard(user_id):
    return shard_directory.get(user_id)

How It Works

┌─────────────┐     ┌──────────────────┐
│   Client    │────▶│  Lookup Service  │
└─────────────┘     │  (Directory)     │
                    │                  │
                    │  user_1 → shard_A│
                    │  user_2 → shard_B│
                    │  user_3 → shard_A│
                    └────────┬─────────┘
                             │
         ┌───────────────────┼───────────────────┐
         ▼                   ▼                   ▼
    ┌─────────┐         ┌─────────┐         ┌─────────┐
    │ Shard A │         │ Shard B │         │ Shard C │
    └─────────┘         └─────────┘         └─────────┘
Pros
  • Maximum flexibility
  • Easy to move individual keys
  • Can balance load dynamically
Cons
  • Directory becomes bottleneck
  • Extra network hop per query
  • Directory must be highly available

Strategy Comparison

Factor Hash Range Directory
Distribution Excellent Variable Controllable
Range Queries Poor Excellent Variable
Adding Shards Requires rehash* Split ranges Update directory
Complexity Simple Simple Complex
Hotspot Risk Low High Low (manual)

*Consistent hashing solves the rehashing problem (covered in lesson 4)

Real-World Examples

MongoDB: Hash + Range

MongoDB supports both strategies and lets you choose based on your use case:

// Hash-based sharding (even distribution)
sh.shardCollection("mydb.users", { user_id: "hashed" })

// Range-based sharding (range queries)
sh.shardCollection("mydb.logs", { timestamp: 1 })

Vitess (YouTube's MySQL Sharding)

YouTube uses Vitess for sharding MySQL. It combines approaches:

Vitess at YouTube

Vitess uses a VSchema (directory-like mapping) combined with hash-based distribution. The VSchema defines how tables are sharded, while Vitess's routing layer (vtgate) handles query routing automatically.

Cassandra: Consistent Hashing

Apache Cassandra uses a token ring with consistent hashing:

-- Cassandra partition key (shard key)
CREATE TABLE users (
    user_id UUID PRIMARY KEY,
    name TEXT,
    email TEXT
);
-- user_id is hashed to determine which node stores the data

Choosing a Strategy

Use Hash-Based When:

  • You need even distribution
  • Most queries are point lookups (by ID)
  • Access patterns are uniform
  • Example: User profiles by user_id

Use Range-Based When:

  • You frequently query by ranges
  • Data has natural ordering
  • You can predict and manage hotspots
  • Example: Time-series data, logs

Use Directory-Based When:

  • You need maximum flexibility
  • Data placement requirements are complex
  • You need to move data frequently
  • Example: Multi-tenant SaaS

Key Takeaways

  • Hash-based - even distribution, poor range queries, simple
  • Range-based - good for ranges, risk of hotspots
  • Directory-based - flexible but complex, adds latency
  • Most systems use consistent hashing - a variation of hash-based that handles resharding gracefully

Next up: Shard Key Selection - The most critical decision in your sharding design.

🧠 Quick Quiz

Test your understanding of this lesson.

1

What is the main advantage of hash-based sharding?

2

When is range-based sharding most appropriate?

3

What is the purpose of a directory/lookup table in directory-based sharding?

What is Database Sharding?