Database Sharding
MYSQL
Django
Database Management
Database Optimization

How can I achieve database sharding for my MYSQL database with Django

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Django can talk to many MySQL databases, but it does not perform automatic sharding for you. If you want sharding, you have to choose a shard key, route reads and writes deliberately, and accept that some ORM conveniences become harder once related data lives on different physical databases.

Start with the Shard Key

The most important design choice is not the router class. It is the shard key: the rule that decides which database owns a given row.

Common shard keys include:

  • tenant or customer id
  • user id
  • geographic region
  • a hash of a stable identifier

A good shard key should do two things:

  • keep related data that is queried together on the same shard
  • distribute load reasonably evenly across shards

If your workload needs frequent joins or reports across the entire dataset, sharding may create more pain than it removes.

Configure Multiple Databases in Django

Django already supports multiple database connections.

python
1DATABASES = {
2    "default": {},
3    "shard_0": {
4        "ENGINE": "django.db.backends.mysql",
5        "NAME": "app_shard_0",
6        "USER": "app",
7        "PASSWORD": "secret",
8        "HOST": "mysql-shard-0",
9        "PORT": "3306",
10    },
11    "shard_1": {
12        "ENGINE": "django.db.backends.mysql",
13        "NAME": "app_shard_1",
14        "USER": "app",
15        "PASSWORD": "secret",
16        "HOST": "mysql-shard-1",
17        "PORT": "3306",
18    },
19}

This only defines connections. It does not create a sharding policy yet.

Route with a Database Router

A Django database router is the usual entry point for shard selection.

python
1class ShardRouter:
2    def _db_for_tenant(self, tenant_id: int) -> str:
3        return f"shard_{tenant_id % 2}"
4
5    def db_for_read(self, model, **hints):
6        tenant_id = hints.get("tenant_id")
7        if tenant_id is None:
8            return None
9        return self._db_for_tenant(tenant_id)
10
11    def db_for_write(self, model, **hints):
12        tenant_id = hints.get("tenant_id")
13        if tenant_id is None:
14            return None
15        return self._db_for_tenant(tenant_id)
16
17    def allow_relation(self, obj1, obj2, **hints):
18        return obj1._state.db == obj2._state.db
19
20    def allow_migrate(self, db, app_label, model_name=None, **hints):
21        return db.startswith("shard_")

Then register it:

python
DATABASE_ROUTERS = ["project.dbrouters.ShardRouter"]

This works only if your application can provide enough context, such as the current tenant id, when it makes database decisions.

Keep Shard Selection Visible in Application Code

A common mistake is trying to hide sharding completely from application logic. In practice, it is often clearer to make the shard choice explicit at key boundaries.

python
1customer = Customer(id=101, name="Acme")
2customer.save(using="shard_1")
3
4orders = Order.objects.using("shard_1").filter(customer_id=101)

This is more verbose, but it makes data placement obvious. That clarity helps during debugging, migrations, and operational support.

What Gets Harder After Sharding

Sharding is not only a router problem. It changes the operational model.

Expect extra complexity around:

  • migrations that must run on every shard
  • global reports that need fan-out queries or a separate analytics path
  • foreign keys across shards, which are usually not practical
  • rebalancing data when a shard fills up or the key distribution proves uneven

That is why sharding should be chosen because the workload truly needs it, not because the architecture sounds advanced.

Simpler Alternatives May Be Better

Before sharding, many Django/MySQL systems still have headroom through:

  • proper indexing
  • query optimization
  • caching
  • read replicas
  • partitioning inside MySQL

Sharding is usually a later-stage scaling move, not the first one.

Common Pitfalls

Starting with router code before validating the shard key against real query patterns is the most common mistake.

Assuming the Django ORM will stay equally convenient across shard boundaries is another. Cross-shard relations and aggregates are much harder.

Trying to hide sharding completely often makes production debugging worse, not better.

Finally, do not adopt sharding before simpler scaling tools have been measured and exhausted.

Summary

  • Django supports multiple MySQL databases, but it does not shard data automatically
  • the shard key matters more than the router implementation
  • use multiple database settings plus explicit routing logic
  • keep shard placement visible enough that your team can reason about it operationally
  • choose sharding only when the workload clearly benefits from horizontal partitioning

Course illustration
Course illustration

All Rights Reserved.