Database Sharding & Replication
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 বাড়ায়।
Database Replication
Replication মানে same data multiple servers এ রাখা। Primary (Master) server এ write হয়, Replica (Slave) servers এ copy propagate হয়।
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 Case | Tradeoff |
|---|---|---|---|
| Synchronous | Primary AND all replicas acknowledge করলেন write complete | Strong consistency needed | Slow writes |
| Asynchronous | Primary acknowledge করলেনই write complete, replicas background এ sync | High write throughput | Possible data lag |
| Semi-sync | কমপক্ষে ১টা replica acknowledge করলেন complete | Balance between both | Good compromise |
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।
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-13. Directory-based Sharding
একটা lookup table রাখা হয় — কোন key কোন shard এ আছে। Flexible কিন্তু lookup table নিজেই bottleneck হতে পারে।
| Strategy | Distribution | Resharding | Query | Best For |
|---|---|---|---|---|
| Range | Uneven (hotspot) | Easy | Range queries easy | Time-series data |
| Hash | Even | Hard (data moves) | Range queries hard | User data, general |
| Consistent Hash | Even | Minimal movement | OK | Dynamic scaling |
| Directory | Flexible | Easy | Lookup overhead | Custom rules |
Code Examples
Node.js — Read/Write Splitting
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
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)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।
SUMMARY — আজকে যা শিখলাম
| Concept | এক লাইনে |
|---|---|
| Replication | Same data, multiple servers — read scaling + availability |
| Sharding | Different data, multiple servers — write/storage scaling |
| Hash Sharding | Even distribution, good for user data |
| Range Sharding | Range queries সহজ, hotspot ঝুঁকি আছে |
| Consistent Hashing | New shard add এ minimal data movement |
| Read/Write Split | Write → Primary, Read → Replica |
| Cross-shard Problem | Sharding এর সবচেয়ে বড় challenge |