पाठ 16 / 32

Sharding by Tenant

यह पाठ अभी हिंदी में अनुवादित नहीं है, इसलिए अंग्रेज़ी में दिखाया जा रहा है।

Use the tenant as a shard key, compare hash-based and directory-based placement, and explain why most queries then stay on one shard.

लेखक: · 15 साल से वेब डेवलपर

The tenant is a natural shard key

When one database can no longer hold all pooled tenants, you shard: split tenants across several database clusters. In B2B SaaS the tenant is an excellent shard key, because almost every query is about one tenant, so it touches exactly one shard and needs no cross-shard joins. There are two main ways to place a tenant. Hash-based placement computes hash(tenant_id) mod N; it needs no lookup table, but changing N moves many tenants, and you cannot choose where a specific tenant goes. Directory-based placement stores tenant -> shard in a catalogue; it costs a lookup (easily cached) but lets you put a huge tenant on its own shard, move tenants one at a time and add shards without reshuffling everyone. Many systems combine them: hash for the default, directory entries for exceptions. The hard cases are cross-tenant features such as global search or analytics, which usually read from a separate warehouse instead of querying every shard.

Placing a tenant on a shard

A request's tenant is looked up in the directory; tenants without an explicit entry fall back to a stable hash; the request then goes to exactly one shard.

Three stages: tenant id, directory or hash lookup, and a single target shard.
Figure 6.1: tenant id -> directory lookup (hash fallback) -> one shard

Directory with a hash fallback

A stable hash spreads ordinary tenants; the directory pins special ones.

import hashlib

SHARDS = ["shard-0", "shard-1", "shard-2", "shard-3"]

def hash_shard(tenant_id: str) -> str:
    # Stable hash: Python's built-in hash() changes between runs, so avoid it
    h = int(hashlib.sha256(tenant_id.encode()).hexdigest(), 16)
    return SHARDS[h % len(SHARDS)]

# Directory (lookup table) placement: explicit, and movable later
DIRECTORY = {"bigbank": "shard-3"}

def place(tenant_id: str) -> str:
    return DIRECTORY.get(tenant_id) or hash_shard(tenant_id)

for t in ["acme", "zen", "bigbank"]:
    print(t, "->", place(t))

Output:

acme -> shard-3
zen -> shard-1
bigbank -> shard-3

Python's hash() is not stable

For strings, Python randomises hash() per process unless PYTHONHASHSEED is fixed, so two servers would disagree about placement. Use a real digest such as SHA-256 or a dedicated hash like xxHash or MurmurHash for anything persisted or shared between processes.

त्वरित जाँच: Why do many SaaS systems prefer a directory (lookup table) over pure `hash mod N` for tenant placement?

  • Directories make every query cross-shard
  • Hashing cannot handle string ids
  • A directory lets you move or isolate individual tenants and add shards without reshuffling everyone
  • Hashes are slower to compute than a database lookup
Answer

A directory lets you move or isolate individual tenants and add shards without reshuffling everyone — With mod N, changing N remaps many tenants and you cannot pin a specific tenant. A directory makes placement an explicit, per-tenant, changeable fact.