DEV Community

SHARON SHAJI
SHARON SHAJI

Posted on

MySQL Sharding

Why, When & How

As applications grow, the database often becomes one of the biggest scalability bottlenecks.

You may start with:

Application
     |
     v
  MySQL
Enter fullscreen mode Exit fullscreen mode

Everything works perfectly.

Then traffic increases.

You add read replicas:

                 Application
                      |
                +-----+-----+
                |           |
                v           v
             Primary     Replicas
Enter fullscreen mode Exit fullscreen mode

That helps with read scalability.

But eventually another question appears:

What happens when the data itself becomes too large for a single MySQL instance?

This is where database sharding becomes relevant.


What Is MySQL Sharding?

Database sharding is a horizontal data-partitioning technique where a large logical database is split across multiple independent database instances.

Each database instance is called a shard.

For example, instead of storing 1 billion users on one MySQL server:

                    MySQL
                      |
              1 Billion Users
Enter fullscreen mode Exit fullscreen mode

we can distribute the data:

flowchart TD
    A[Application] --> B[Sharding Layer]

    B --> C[Shard 1]
    B --> D[Shard 2]
    B --> E[Shard 3]

    C --> C1[Users 1 - 333M]
    D --> D1[Users 334M - 666M]
    E --> E1[Users 667M - 1B]

The application sees one logical dataset, while physically the data is distributed across multiple MySQL servers.


Why Do We Need Sharding?

The main reason is horizontal scalability.

A single MySQL instance has finite resources:

CPU
RAM
Disk
IOPS
Network
Connection capacity
Write throughput
Enter fullscreen mode Exit fullscreen mode

You can scale vertically:

Small Server
     |
     v
Bigger Server
     |
     v
Even Bigger Server
Enter fullscreen mode Exit fullscreen mode

But eventually you hit practical and economic limits.

Sharding allows you to distribute the workload:

flowchart LR
    A[Application] --> B[Shard Router]

    B --> C[MySQL Shard 1]
    B --> D[MySQL Shard 2]
    B --> E[MySQL Shard 3]
    B --> F[MySQL Shard 4]

Instead of one server handling the entire dataset, multiple servers handle different portions of it.


Replication vs Sharding

This is one of the most important concepts to understand.

MySQL Replication

Replication creates copies of the same data.

flowchart TD
    A[Primary MySQL] --> B[Replica 1]
    A --> C[Replica 2]
    A --> D[Replica 3]

Conceptually:

Primary
  |
  +----> Replica 1
  |
  +----> Replica 2
  |
  +----> Replica 3
Enter fullscreen mode Exit fullscreen mode

Each replica contains a copy of the dataset.

Replication can help with:

  • Read scaling
  • High availability
  • Failover
  • Disaster recovery
  • Reducing read pressure on the primary

But replication does not split the dataset.


The Problem With Replication

Imagine your database has:

5 TB of data
Enter fullscreen mode Exit fullscreen mode

You have:

1 Primary
3 Replicas
Enter fullscreen mode Exit fullscreen mode

Conceptually, you now have multiple copies of the same 5 TB dataset.

More importantly, if the architecture has a single write primary, the write workload still has to pass through that primary.

Replication does not automatically solve:

  • Primary write bottlenecks
  • Extremely large indexes
  • Storage limitations on one node
  • Large working sets
  • Single-node CPU limitations
  • Single-node I/O limitations

This is where sharding becomes interesting.


Sharding vs Replication

A simple way to remember it:

Replication

Make copies of the data.

flowchart TD
    A[Same Dataset] --> B[Primary]
    A --> C[Replica 1]
    A --> D[Replica 2]

Sharding

Split the data.

flowchart TD
    A[Complete Dataset] --> B[Shard 1]
    A --> C[Shard 2]
    A --> D[Shard 3]

And in real-world systems, you can combine both.


Sharding + Replication

A production architecture can look like this:

flowchart TD
    A[Application] --> B[Shard Router]

    B --> S1[Shard 1 Primary]
    B --> S2[Shard 2 Primary]
    B --> S3[Shard 3 Primary]

    S1 --> R1A[Shard 1 Replica]
    S1 --> R1B[Shard 1 Replica]

    S2 --> R2A[Shard 2 Replica]
    S2 --> R2B[Shard 2 Replica]

    S3 --> R3A[Shard 3 Replica]
    S3 --> R3B[Shard 3 Replica]

Now we have:

Data Distribution
        +
Read Scaling
        +
High Availability
Enter fullscreen mode Exit fullscreen mode

How Does Sharding Work?

The most important concept is the shard key.

The shard key determines where a particular record should be stored.

For example:

user_id
Enter fullscreen mode Exit fullscreen mode

We could use:

user_id % 4
Enter fullscreen mode Exit fullscreen mode

Conceptually:

User ID    Shard
----------------
1001       Shard 1
1002       Shard 2
1003       Shard 3
1004       Shard 4
1005       Shard 1
Enter fullscreen mode Exit fullscreen mode

The routing process looks like:

flowchart LR
    A[User ID] --> B[Hash / Routing Function]
    B --> C[Shard 1]
    B --> D[Shard 2]
    B --> E[Shard 3]
    B --> F[Shard 4]

The application or a dedicated routing layer determines the correct shard.


Common Sharding Strategies

There are several ways to distribute data.

1. Range-Based Sharding

Data is divided according to ranges.

Example:

Shard 1 → user_id 1 - 1,000,000

Shard 2 → user_id 1,000,001 - 2,000,000

Shard 3 → user_id 2,000,001 - 3,000,000
Enter fullscreen mode Exit fullscreen mode

Diagram:

User IDs

1M              2M              3M
|---------------|---------------|
   Shard 1         Shard 2         Shard 3
Enter fullscreen mode Exit fullscreen mode

Advantages

  • Easy to understand
  • Easy to implement
  • Range queries can be efficient

Problems

  • Hotspots can occur
  • Data distribution may become uneven
  • Rebalancing can become difficult

2. Hash-Based Sharding

A hash function determines the shard.

For example:

hash(user_id) % number_of_shards
Enter fullscreen mode Exit fullscreen mode

Conceptually:

flowchart LR
    A[User ID] --> B[Hash Function]
    B --> C[Shard 1]
    B --> D[Shard 2]
    B --> E[Shard 3]
    B --> F[Shard 4]

Advantages

  • Usually distributes data more evenly
  • Reduces predictable hotspots

Problems

  • Range queries become harder
  • Changing the number of shards can cause significant data movement with naive modulo-based schemes

3. Consistent Hashing

Consistent hashing maps keys onto a hash ring.

                    Shard 1
                       ●
                 /           \
                /             \
           ●                       ●
       Shard 3                 Shard 2
                \             /
                 \           /
                       ●
                    Hash Ring
Enter fullscreen mode Exit fullscreen mode

The benefit is that adding or removing nodes can require less data movement compared with a naive modulo approach.


Choosing a Good Shard Key

Choosing the shard key is one of the most important architectural decisions.

A poor shard key can create serious problems.

A good shard key generally needs to provide:

  • Good distribution
  • High cardinality
  • Predictable routing
  • Alignment with common query patterns
  • Reasonable data locality

For example, these may be problematic depending on the workload:

country
gender
status
Enter fullscreen mode Exit fullscreen mode

because many records can have the same value.

Potentially better candidates include:

user_id
tenant_id
customer_id
account_id
Enter fullscreen mode Exit fullscreen mode

But there is no universally correct shard key.

The correct choice depends on how the application accesses the data.


Multi-Tenant Applications

Sharding becomes particularly interesting for SaaS applications.

Imagine:

Tenant A
Tenant B
Tenant C
Tenant D
...
Tenant 10,000
Enter fullscreen mode Exit fullscreen mode

A tenant-based routing model could look like:

flowchart TD
    A[Application] --> B[Tenant Router]

    B --> C[Shard 1]
    B --> D[Shard 2]
    B --> E[Shard 3]

    F[Tenant A] --> C
    G[Tenant B] --> D
    H[Tenant C] --> C
    I[Tenant D] --> E

A mapping table can determine where each tenant lives:

Tenant      Shard
-----------------
Tenant A    Shard 1
Tenant B    Shard 2
Tenant C    Shard 1
Tenant D    Shard 3
Enter fullscreen mode Exit fullscreen mode

This can provide useful data locality and, depending on the design, tenant isolation.


The Biggest Advantage: Horizontal Scaling

Without sharding:

flowchart TD
    A[Application] --> B[Single MySQL Server]
    B --> C[Vertical Scaling]

You keep increasing:

CPU
RAM
Storage
IOPS
Enter fullscreen mode Exit fullscreen mode

This is vertical scaling.

With sharding:

flowchart TD
    A[Application] --> B[Shard Router]

    B --> C[Server 1]
    B --> D[Server 2]
    B --> E[Server 3]
    B --> F[Server 4]

You add more database nodes.

This is horizontal scaling.


But Sharding Is NOT Free

This is where many discussions about sharding become misleading.

Sharding solves scalability problems by introducing additional complexity.

You now need to think about:

Database
   +
Routing
   +
Data Modeling
   +
Transactions
   +
Backups
   +
Failover
   +
Monitoring
   +
Rebalancing
   +
Operational Complexity
Enter fullscreen mode Exit fullscreen mode

Cross-Shard Queries

Consider:

SELECT *
FROM orders
WHERE user_id = 12345;
Enter fullscreen mode Exit fullscreen mode

If user_id is the shard key, routing is straightforward.

But consider:

SELECT COUNT(*)
FROM orders
WHERE created_at >= '2026-01-01';
Enter fullscreen mode Exit fullscreen mode

If orders are distributed across multiple shards, the query may need to execute on multiple databases.

flowchart TD
    A[Application Query] --> B[Shard Router]

    B --> C[Shard 1]
    B --> D[Shard 2]
    B --> E[Shard 3]

    C --> F[Partial Result]
    D --> G[Partial Result]
    E --> H[Partial Result]

    F --> I[Aggregate Results]
    G --> I
    H --> I

    I --> J[Final Result]

The system may need to:

  1. Send the query to multiple shards
  2. Collect partial results
  3. Aggregate the results
  4. Return the final result

This is obviously more complicated than querying one database.


Cross-Shard JOINs

Consider:

SELECT *
FROM users u
JOIN orders o
    ON u.id = o.user_id;
Enter fullscreen mode Exit fullscreen mode

If the related data lives on different shards:

flowchart LR
    A[Shard 1] --> C[Cross-Shard JOIN]
    B[Shard 2] --> C

Cross-shard joins can become expensive and difficult to manage.

This is why data modeling becomes extremely important in a sharded architecture.

Ideally, data that is frequently accessed together should have a strategy that keeps it together or avoids expensive distributed joins.


Distributed Transactions

Single-database transactions are relatively straightforward:

START TRANSACTION;

UPDATE accounts
SET balance = balance - 100
WHERE id = 1;

UPDATE accounts
SET balance = balance + 100
WHERE id = 2;

COMMIT;
Enter fullscreen mode Exit fullscreen mode

When records involved in a business transaction are on different shards, coordination becomes more complicated.

Conceptually:

flowchart TD
    A[Distributed Transaction] --> B[Shard 1]
    A --> C[Shard 2]

    B --> D[Update A]
    C --> E[Update B]

    D --> F[Commit Coordination]
    E --> F

Distributed transactions can introduce additional latency and failure modes.

A well-designed sharded application therefore tries to minimize cross-shard transactional operations.


Resharding

Another major challenge is resharding.

Suppose you start with:

Shard 1
Shard 2
Shard 3
Enter fullscreen mode Exit fullscreen mode

Your application grows.

Now you need:

Shard 1
Shard 2
Shard 3
Shard 4
Shard 5
Shard 6
Enter fullscreen mode Exit fullscreen mode

What happens to existing data?

flowchart LR
    A[Existing Shards] --> B[Data Migration / Rebalancing]

    B --> C[New Shard 1]
    B --> D[New Shard 2]
    B --> E[New Shard 3]
    B --> F[New Shard 4]
    B --> G[New Shard 5]
    B --> H[New Shard 6]

You need a carefully designed strategy for:

  • Moving data
  • Keeping writes consistent
  • Handling reads during migration
  • Validating migrated data
  • Switching routing
  • Removing old shard ownership

This is one of the reasons shard-key design matters so much.


When Should You Consider Sharding?

Don't introduce sharding simply because your database is large.

First investigate the actual bottleneck.

1. Can Vertical Scaling Solve It?

Maybe you simply need:

More CPU
More RAM
Faster NVMe
Higher IOPS
Enter fullscreen mode Exit fullscreen mode

If that solves the problem, sharding may be unnecessary complexity.


2. Can Indexing Solve It?

A missing index can make a database appear "too slow."

Check:

EXPLAIN SELECT ...
Enter fullscreen mode Exit fullscreen mode

Also investigate:

  • Slow query logs
  • Index usage
  • Query execution plans
  • Table statistics

3. Can Query Optimization Solve It?

Before sharding, optimize:

Queries
Indexes
Schema
Connection management
Caching
Application access patterns
Enter fullscreen mode Exit fullscreen mode

4. Can Read Replicas Solve It?

If the primary problem is primarily:

READ traffic
Enter fullscreen mode Exit fullscreen mode

read replicas may be enough.

Sharding is not automatically the answer to every scalability problem.


When Does Sharding Become Relevant?

Sharding becomes relevant when you have significant constraints such as:

flowchart TD
    A[Growing Workload] --> B{Bottleneck}

    B --> C[Huge Dataset]
    B --> D[High Write Load]
    B --> E[Storage Limits]
    B --> F[I/O Limits]
    B --> G[CPU Limits]

    C --> H[Consider Sharding]
    D --> H
    E --> H
    F --> H
    G --> H

The exact threshold is workload-dependent.

There is no universal rule such as:

"At 1 TB you must shard."

That would be bad architecture advice.

A 10 TB database with a manageable workload may be easier to operate than a 500 GB database with extreme write traffic and latency requirements.


A Practical MySQL Scaling Journey

A system might evolve gradually.

Stage 1 — Single Database

flowchart LR
    A[Application] --> B[MySQL]

Simple.


Stage 2 — Primary + Replicas

flowchart TD
    A[Application] --> B[Primary]

    B --> C[Replica 1]
    B --> D[Replica 2]

Useful when read traffic increases.


Stage 3 — Add Caching

flowchart TD
    A[Application] --> B[Cache]
    A --> C[MySQL Primary]

    C --> D[Replica 1]
    C --> E[Replica 2]

Frequently accessed data can potentially be served without hitting MySQL.


Stage 4 — Sharding

flowchart TD
    A[Application] --> B[Shard Router]

    B --> C[Shard 1]
    B --> D[Shard 2]
    B --> E[Shard 3]

Now the dataset itself is distributed.


Stage 5 — Sharded + Replicated

flowchart TD
    A[Application] --> B[Shard Router]

    B --> C[Shard 1 Primary]
    B --> D[Shard 2 Primary]
    B --> E[Shard 3 Primary]

    C --> C1[Replica]
    C --> C2[Replica]

    D --> D1[Replica]
    D --> D2[Replica]

    E --> E1[Replica]
    E --> E2[Replica]

Now the architecture combines:

Horizontal Data Distribution
+
Read Scaling
+
High Availability
Enter fullscreen mode Exit fullscreen mode

What Should You Measure Before Sharding?

Don't guess.

Measure.

Important metrics include:

QPS / TPS
Query latency
Write throughput
Storage growth
CPU utilization
Memory pressure
Disk I/O
IOPS
Lock contention
Connection count
Replication lag
Slow queries
Index efficiency
Enter fullscreen mode Exit fullscreen mode

A simplified decision process:

flowchart TD
    A[Performance Problem] --> B[Measure]

    B --> C{Query Problem?}
    C -->|Yes| D[Optimize Query / Index]

    C -->|No| E{Read Bottleneck?}
    E -->|Yes| F[Read Replicas / Cache]

    E -->|No| G{Single Node Resource Limit?}
    G -->|Yes| H{Can Scale Vertically?}

    H -->|Yes| I[Scale Up]
    H -->|No| J[Evaluate Sharding]

    G -->|No| K[Investigate Other Bottleneck]

The Real Question Isn't "Can MySQL Shard?"

The better question is:

Does my application's workload justify the complexity of distributing data across multiple databases?

Because once you introduce sharding, you are no longer solving only a database problem.

You're also solving:

Application Routing
Data Placement
Data Modeling
Distributed Queries
Transactions
Backups
Monitoring
Failover
Rebalancing
Operational Complexity
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

Replication ≠ Sharding

Replication distributes copies of data.

Sharding distributes different portions of data.

Replication primarily helps with:

  • Read scalability
  • High availability
  • Failover
  • Disaster recovery

Sharding primarily helps with:

  • Data distribution
  • Horizontal database scaling
  • Large datasets
  • High write workloads
  • Resource isolation

And they can be combined:

             HIGH SCALE
                 |
       +---------+---------+
       |                   |
   SHARDING           REPLICATION
       |                   |
 Data Distribution    Read Scaling
 Horizontal Scale     High Availability
       |                   |
       +---------+---------+
                 |
          Distributed MySQL
Enter fullscreen mode Exit fullscreen mode

Final Thought

Sharding should be an architectural decision driven by measurable bottlenecks — not a default design pattern for every large application.

Before introducing sharding, understand:

What is actually slow?
        ↓
Where is the bottleneck?
        ↓
Can indexing fix it?
        ↓
Can query optimization fix it?
        ↓
Can caching help?
        ↓
Can read replicas help?
        ↓
Can vertical scaling help?
        ↓
If not...
        ↓
Evaluate Sharding
Enter fullscreen mode Exit fullscreen mode

The goal isn't to build the most complicated database architecture.

The goal is to build an architecture that can reliably handle the workload you actually have.


What would you choose?

For a rapidly growing MySQL application, how would you approach the scaling problem?

Read Replicas → Partitioning → Sharding → or a combination?

Share your approach in the comments. 👇


Top comments (0)