System DesignMastery
--Core Components — মূল বিল্ডিং ব্লক

Database Sharding & Replication

Duration৬০-৯০ মিনিট
LevelAdvanced
FocusDatabase Scaling
001Core Concept

Database Scaling সমস্যা

একটা single database server এ ১ কোটি user এর data রাখা যায়। কিন্তু ১০ কোটি user হলে? Query slow হয়, disk full হয়, single server overload হয়। এই সমস্যা solve করার দুটো প্রধান উপায় হলো Replication এবং Sharding

দুটো Concept

Replication: Same data কে multiple servers এ copy করা। Read performance বাড়ায়, availability বাড়ায়।
Sharding (Horizontal Partitioning): Data কে multiple servers এ ভাগ করে রাখা। প্রতিটা shard শুধু data এর একটা অংশ রাখে। Storage এবং write performance বাড়ায়।

002Replication

Database Replication

Replication মানে same data multiple servers এ রাখা। Primary (Master) server এ write হয়, Replica (Slave) servers এ copy propagate হয়।

App ServerReads & WritesWRITEPrimary(Master)All writes hereasync copyReplica 1Replica 2Replica 3READ from replicas

Replication সুবিধা

Read scaling (replicas থেকে read করুন), High availability (Primary fail → Replica promote হয়), Read-heavy apps এর জন্য excellent।

Replication সীমাবদ্ধতা

Replication lag: Primary তে write হওয়ার পর Replica তে sync হতে সময় লাগে। Write bottleneck: সব write primary তে যায়। Storage: সব servers এ same data।

Typeকীভাবে কাজ করেUse CaseTradeoff
SynchronousPrimary AND all replicas acknowledge করলেন write completeStrong consistency neededSlow writes
AsynchronousPrimary acknowledge করলেনই write complete, replicas background এ syncHigh write throughputPossible data lag
Semi-syncকমপক্ষে ১টা replica acknowledge করলেন completeBalance between bothGood compromise
003Sharding

Database Sharding

Sharding মানে data কে multiple database servers এ ভাগ করে রাখা। প্রতিটা server (shard) data এর একটা subset রাখে। ১ কোটি users থাকলে, Shard 1 এ প্রথম ৩৩ লাখ, Shard 2 এ পরের ৩৩ লাখ, Shard 3 এ বাকি ৩৪ লাখ।

App ServerShard Router"Which shardfor user_id=500?"Shard 1user_id: 1 → 3,333,333Shard 2user_id: 3,333,334 → 6,666,666Shard 3user_id: 6,666,667 → 10,000,000
004Shard Strategies

Sharding Strategies

1. Range-based Sharding

Data কে value range অনুযায়ী ভাগ করা। সহজ কিন্তু hotspot হতে পারে (নতুন users সবাই একই shard এ যায়)।

Range Sharding Problem

user_id 1-1M → Shard 1, 1M-2M → Shard 2। কিন্তু নতুন users সবসময় Shard 2 এ যাবেন। Shard 1 idle, Shard 2 overloaded — uneven distribution।

2. Hash-based Sharding

Shard key hash করে shard select করা। Even distribution দেয়। সবচেয়ে popular।

hash_sharding.py
import hashlib

def get_shard(user_id: int, num_shards: int = 3) -> int:
    # user_id hash করে shard number বের করুন
    hash_val = int(hashlib.md5(str(user_id).encode()).hexdigest(), 16)
    return hash_val % num_shards

# Consistent Hashing — shard add/remove এ data movement কমায়
import bisect

class ConsistentHashRing:
    def __init__(self, replicas=100):
        self.replicas = replicas
        self.ring = {}
        self.sorted_keys = []

    def add_shard(self, shard_name):
        for i in range(self.replicas):
            key = hash(f"{shard_name}:{i}")
            self.ring[key] = shard_name
            bisect.insort(self.sorted_keys, key)

    def get_shard(self, key):
        if not self.ring: return None
        h = hash(key)
        idx = bisect.bisect(self.sorted_keys, h) % len(self.sorted_keys)
        return self.ring[self.sorted_keys[idx]]

ring = ConsistentHashRing()
ring.add_shard("shard-1")
ring.add_shard("shard-2")
ring.add_shard("shard-3")

print(ring.get_shard("user:12345"))   # → shard-2
print(ring.get_shard("user:67890"))   # → shard-1

3. Directory-based Sharding

একটা lookup table রাখা হয় — কোন key কোন shard এ আছে। Flexible কিন্তু lookup table নিজেই bottleneck হতে পারে।

StrategyDistributionReshardingQueryBest For
RangeUneven (hotspot)EasyRange queries easyTime-series data
HashEvenHard (data moves)Range queries hardUser data, general
Consistent HashEvenMinimal movementOKDynamic scaling
DirectoryFlexibleEasyLookup overheadCustom rules
005Common Problems

Sharding এর সমস্যা

Cross-Shard Queries

"সব users যাদের age > 25 দেখাও" — এটা সব shards এ query করতে হবে এবং results merge করতে হবে। এটা complex এবং slow। Shard key দিয়ে query হলে সহজ, cross-shard হলে কঠিন।

Cross-Shard Transactions

User A Shard 1 এ, User B Shard 2 এ। A থেকে B তে money transfer — এটা distributed transaction। Two-Phase Commit লাগে। Complex, slow, আর potential deadlock।

Resharding Problem

৩টা shard থেকে ৪টা shard এ move করলেন data redistribute করতে হবে। Application downtime possible। Consistent hashing এই pain কমায়।

Best Practice

Shard key choose করা সবচেয়ে important decision। User-based query pattern analyze করুন। user_id সাধারণত good shard key — queries mostly per-user। Avoid sharding by যে field এ cross-shard query বেশি হবে।

006Code Examples

Code Examples

Node.js — Read/Write Splitting

db_routing.js
const { Pool } = require('pg');

// Primary (write) database
const primary = new Pool({ connectionString: process.env.DB_PRIMARY_URL });

// Read replicas — multiple
const replicas = [
  new Pool({ connectionString: process.env.DB_REPLICA1_URL }),
  new Pool({ connectionString: process.env.DB_REPLICA2_URL }),
];
let replicaIndex = 0;

// Round-robin replica selection
function getReadPool() {
  const pool = replicas[replicaIndex % replicas.length];
  replicaIndex++;
  return pool;
}

// Use primary for writes
async function writeQuery(sql, params) {
  console.log('✍️ Writing to PRIMARY');
  return primary.query(sql, params);
}

// Use replica for reads
async function readQuery(sql, params) {
  console.log('📖 Reading from REPLICA');
  return getReadPool().query(sql, params);
}

// Usage
await writeQuery('INSERT INTO users...', [name, email]);
await readQuery('SELECT * FROM users WHERE id=$1', [userId]);

Python — Shard Router

shard_router.py
import psycopg2

SHARD_CONFIGS = {
    0: {'host': 'shard0.db.com', 'dbname': 'users_0'},
    1: {'host': 'shard1.db.com', 'dbname': 'users_1'},
    2: {'host': 'shard2.db.com', 'dbname': 'users_2'},
}

def get_connection(user_id: int):
    shard_num = user_id % len(SHARD_CONFIGS)
    config = SHARD_CONFIGS[shard_num]
    print(f"User {user_id} → Shard {shard_num}")
    return psycopg2.connect(**config)

def get_user(user_id: int):
    with get_connection(user_id) as conn:
        with conn.cursor() as cur:
            cur.execute("SELECT * FROM users WHERE id=%s", (user_id,))
            return cur.fetchone()

print(get_user(12345))   # User 12345 → Shard 0 (12345 % 3 = 0)
print(get_user(12346))   # User 12346 → Shard 1 (12346 % 3 = 1)
007Interview Prep

Common Interview Questions

Q1: Sharding vs Replication পার্থক্য?

উত্তর: Replication = same data, multiple servers (read scaling, availability)। Sharding = different data, multiple servers (write scaling, storage scaling)। Real systems এ দুটো একসাথে use হয়: প্রতিটা shard এর নিজস্ব replica থাকে।

Q2: Consistent Hashing কীভাবে কাজ করে?

উত্তর: Regular hash (user_id % n_shards) এ n বাড়লে প্রায় সব keys এর shard বদলায় — massive data movement। Consistent hashing এ n বাড়লে শুধু k/n keys move হয় (k = total keys, n = shards)। Virtual nodes দিয়ে even distribution।

Q3: Shard key কীভাবে select করবেন?

উত্তর: Good shard key: High cardinality (unique values অনেক), Even distribution, Queries mostly এই key এ, Cross-shard queries কম। Bad shard key: Gender (only 2 values → hotspot), Created_at (new data all in same shard)। user_id সাধারণত excellent shard key।

008Lesson Summary

SUMMARY — আজকে যা শিখলাম

Conceptএক লাইনে
ReplicationSame data, multiple servers — read scaling + availability
ShardingDifferent data, multiple servers — write/storage scaling
Hash ShardingEven distribution, good for user data
Range ShardingRange queries সহজ, hotspot ঝুঁকি আছে
Consistent HashingNew shard add এ minimal data movement
Read/Write SplitWrite → Primary, Read → Replica
Cross-shard ProblemSharding এর সবচেয়ে বড় challenge
009Knowledge Check
010Assignments
011Practical Lab