一键导入
distributed-systems-partitioning-sharding
Data partitioning and sharding strategies including hash-based, range-based, consistent hashing, and rebalancing
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Data partitioning and sharding strategies including hash-based, range-based, consistent hashing, and rebalancing
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Index of Build Systems Skills
Coordination patterns for distributed dataflow systems including barriers, epochs, and distributed snapshots
Windowing, sessionization, time-series aggregation, and late data handling for streaming systems
Comprehensive guide to GNU Debugger (GDB) for debugging C/C++/Rust programs. Covers breakpoints, stack traces, variable inspection, TUI mode, .gdbinit customization, Python scripting, remote debugging, and core file analysis.
Paxos consensus algorithm including Basic Paxos, Multi-Paxos, roles, phases, and practical implementations
Gossip protocols for disseminating information, failure detection, and eventual consistency in large-scale distributed systems
| name | distributed-systems-partitioning-sharding |
| description | Data partitioning and sharding strategies including hash-based, range-based, consistent hashing, and rebalancing |
Scope: Partitioning strategies, consistent hashing, rebalancing, hotspots, practical implementations Lines: ~280 Last Updated: 2025-10-27 Format Version: 1.0 (Atomic)
Activate this skill when:
Problem: Single node can't handle all data/load
Solution: Split data across multiple nodes
Benefits:
✅ Scalability (more nodes = more capacity)
✅ Performance (parallel queries)
✅ Fault isolation (failure affects subset)
Partitioning: General term for splitting data Sharding: Partitioning in distributed databases
Often used interchangeably
Approach: Hash key to determine partition
import hashlib
class HashPartitioner:
"""Hash-based partitioning"""
def __init__(self, num_partitions):
self.num_partitions = num_partitions
self.partitions = [[] for _ in range(num_partitions)]
def get_partition(self, key):
"""Determine partition for key"""
hash_val = int(hashlib.md5(key.encode()).hexdigest(), 16)
return hash_val % self.num_partitions
def write(self, key, value):
"""Write to appropriate partition"""
partition_id = self.get_partition(key)
self.partitions[partition_id].append((key, value))
def read(self, key):
"""Read from appropriate partition"""
partition_id = self.get_partition(key)
for k, v in self.partitions[partition_id]:
if k == key:
return v
return None
# Usage
partitioner = HashPartitioner(num_partitions=4)
partitioner.write('user:123', {'name': 'Alice'})
partitioner.write('user:456', {'name': 'Bob'})
print(partitioner.read('user:123')) # {'name': 'Alice'}
Pros:
Cons:
Approach: Assign key ranges to partitions
class RangePartitioner:
"""Range-based partitioning"""
def __init__(self, ranges):
# ranges = [(min, max, partition_id), ...]
self.ranges = sorted(ranges, key=lambda x: x[0])
self.partitions = {}
def get_partition(self, key):
"""Find partition for key"""
for min_key, max_key, partition_id in self.ranges:
if min_key <= key < max_key:
return partition_id
raise KeyError(f"No partition for key {key}")
def range_query(self, start_key, end_key):
"""Query range of keys"""
results = []
# Find all relevant partitions
for min_key, max_key, partition_id in self.ranges:
if max_key >= start_key and min_key < end_key:
# This partition contains relevant data
results.extend(self._query_partition(partition_id, start_key, end_key))
return results
# Usage
partitioner = RangePartitioner([
('A', 'G', 0), # Partition 0: A-F
('G', 'M', 1), # Partition 1: G-L
('M', 'S', 2), # Partition 2: M-R
('S', 'Z', 3), # Partition 3: S-Z
])
Pros:
Cons:
Approach: Hash keys and nodes onto ring, walk clockwise
import hashlib
import bisect
class ConsistentHashing:
"""Consistent hashing with virtual nodes"""
def __init__(self, num_virtual_nodes=150):
self.num_virtual_nodes = num_virtual_nodes
self.ring = [] # Sorted list of (hash, node_id)
self.nodes = set()
def _hash(self, key):
"""Hash key to position on ring"""
return int(hashlib.md5(key.encode()).hexdigest(), 16)
def add_node(self, node_id):
"""Add node to ring"""
self.nodes.add(node_id)
# Add virtual nodes
for i in range(self.num_virtual_nodes):
virtual_key = f"{node_id}:{i}"
hash_val = self._hash(virtual_key)
bisect.insort(self.ring, (hash_val, node_id))
def remove_node(self, node_id):
"""Remove node from ring"""
self.nodes.discard(node_id)
# Remove virtual nodes
self.ring = [(h, n) for h, n in self.ring if n != node_id]
def get_node(self, key):
"""Find node responsible for key"""
if not self.ring:
return None
hash_val = self._hash(key)
# Find first node clockwise
idx = bisect.bisect_right(self.ring, (hash_val, ''))
if idx == len(self.ring):
idx = 0
return self.ring[idx][1]
# Usage
ch = ConsistentHashing()
ch.add_node('node1')
ch.add_node('node2')
ch.add_node('node3')
print(ch.get_node('user:123')) # node2
print(ch.get_node('user:456')) # node1
# Add node - minimal keys moved
ch.add_node('node4')
Pros:
Cons:
Celebrity effect:
User "celebrity" has 10M followers
All queries for "celebrity" → Same partition → Overload
class AdaptivePartitioner:
"""Split partitions that become hot"""
def __init__(self):
self.partitions = {}
self.access_counts = {}
def access(self, key):
self.access_counts[key] = self.access_counts.get(key, 0) + 1
# Split if too hot
if self.access_counts[key] > 10000:
self._split_hot_key(key)
def _split_hot_key(self, key):
"""Replicate hot key to multiple partitions"""
# Create replicas
for i in range(3):
replica_key = f"{key}:replica:{i}"
self.partitions[replica_key] = self.partitions[key]
def read_with_randomization(key):
"""Read from random replica of hot key"""
replicas = get_replicas(key)
replica = random.choice(replicas)
return replica.read(key)
Triggers:
- Node added (scale out)
- Node removed (failure or scale down)
- Load imbalance detected
1. Stop-the-World (simple but downtime):
def rebalance_stop_the_world(old_partitions, new_num_partitions):
"""Rebalance with downtime"""
# 1. Stop writes
stop_writes()
# 2. Redistribute data
new_partitions = redistribute(old_partitions, new_num_partitions)
# 3. Resume writes
resume_writes()
return new_partitions
2. Online Rebalancing (no downtime):
class OnlineRebalancer:
"""Rebalance without downtime"""
def rebalance(self, target_partitions):
# 1. Create new partitions (in parallel with old)
new_partitions = self._create_partitions(target_partitions)
# 2. Start dual-writing (to both old and new)
self._enable_dual_write()
# 3. Copy existing data to new partitions
self._copy_data(new_partitions)
# 4. Switch reads to new partitions
self._switch_reads(new_partitions)
# 5. Stop dual-writing, remove old partitions
self._disable_dual_write()
self._remove_old_partitions()
Partition by user_id, but want to query by email
class LocalIndexPartitioner:
"""Local secondary index per partition"""
def __init__(self, num_partitions):
self.partitions = [
{'data': {}, 'email_index': {}}
for _ in range(num_partitions)
]
def write(self, user_id, email, data):
"""Write with local index"""
partition_id = hash(user_id) % len(self.partitions)
partition = self.partitions[partition_id]
partition['data'][user_id] = data
partition['email_index'][email] = user_id
def query_by_email(self, email):
"""Scatter-gather across all partitions"""
for partition in self.partitions:
if email in partition['email_index']:
user_id = partition['email_index'][email]
return partition['data'][user_id]
return None
class GlobalIndexPartitioner:
"""Separate global secondary index"""
def __init__(self, num_partitions):
self.data_partitions = [{} for _ in range(num_partitions)]
self.global_email_index = {} # email → (partition_id, user_id)
def write(self, user_id, email, data):
"""Write to data partition and global index"""
partition_id = hash(user_id) % len(self.data_partitions)
self.data_partitions[partition_id][user_id] = data
self.global_email_index[email] = (partition_id, user_id)
def query_by_email(self, email):
"""Direct lookup via global index"""
if email in self.global_email_index:
partition_id, user_id = self.global_email_index[email]
return self.data_partitions[partition_id][user_id]
return None
// Enable sharding
sh.enableSharding("mydb")
// Shard collection by user_id (hash)
sh.shardCollection("mydb.users", {user_id: "hashed"})
// Or by range
sh.shardCollection("mydb.orders", {order_date: 1})
-- Partition key determines data distribution
CREATE TABLE users (
user_id UUID,
name TEXT,
PRIMARY KEY (user_id) -- Partition key
);
-- Composite partition key
CREATE TABLE user_posts (
user_id UUID,
post_date DATE,
post_id UUID,
content TEXT,
PRIMARY KEY ((user_id, post_date), post_id) -- Partition key: (user_id, post_date)
);
| Strategy | Distribution | Range Queries | Rebalancing | Hotspots |
|---|---|---|---|---|
| Hash | ✅ Even | ❌ Hard | ⚠️ Rehash all | ✅ Rare |
| Range | ⚠️ Uneven | ✅ Easy | ✅ Easy | ❌ Common |
| Consistent Hash | ✅ Even | ❌ Hard | ✅ Minimal | ✅ Rare |
distributed-systems-replication-strategies - Data replicationdistributed-systems-consensus-raft - Consensus for configurationdistributed-systems-eventual-consistency - Consistency modelsdistributed-systems-distributed-locks - Coordination primitivesLast Updated: 2025-10-27