Database Sharding Erklaert
Database Sharding: Horizontale Skalierung
Sharding verteilt Daten auf mehrere Datenbankserver für massive Skalierung. Lernen Sie Strategien, Implementierung und wann Sharding sinnvoll ist.
Was ist Sharding?
OHNE SHARDING (Vertical Scaling)
┌─────────────────────────────────┐
│ SINGLE DATABASE │
│ ┌─────────────────────┐ │
│ │ Alle 100 Mio. User │ │
│ │ Alle Bestellungen │ │
│ │ Alle Produkte │ │
│ └─────────────────────┘ │
│ Limitiert durch Hardware │
└─────────────────────────────────┘
MIT SHARDING (Horizontal Scaling)
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ SHARD 1 │ │ SHARD 2 │ │ SHARD 3 │ │ SHARD 4 │
│ User A-F │ │ User G-L │ │ User M-R │ │ User S-Z │
│ 25 Mio. │ │ 25 Mio. │ │ 25 Mio. │ │ 25 Mio. │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │ │ │
└──────────────┴──────────────┴──────────────┘
│
Application
(Shard Router)
Sharding-Strategien
1. RANGE-BASED SHARDING ──────────────────── Shard 1: user_id 1 - 1.000.000 Shard 2: user_id 1.000.001 - 2.000.000 Shard 3: user_id 2.000.001 - 3.000.000 ✅ Einfach zu verstehen ✅ Range-Queries effizient ❌ Hotspots bei neuen Daten (alle schreiben auf letzten Shard) ❌ Ungleiche Verteilung möglich 2. HASH-BASED SHARDING ──────────────────── shard_id = hash(user_id) % num_shards Beispiel: hash(12345) % 4 = 1 → Shard 1 hash(67890) % 4 = 2 → Shard 2 ✅ Gleichmäßige Verteilung ✅ Keine Hotspots ❌ Range-Queries ineffizient (alle Shards anfragen) ❌ Resharding aufwendig 3. DIRECTORY-BASED SHARDING ───────────────────────── Lookup-Tabelle: user_id → shard_id ┌──────────────────────┐ │ user_id │ shard_id │ │─────────┼────────────│ │ 12345 │ shard_3 │ │ 67890 │ shard_1 │ │ ... │ ... │ └──────────────────────┘ ✅ Maximale Flexibilität ✅ Einfaches Resharding ❌ Directory ist Single Point of Failure ❌ Zusätzlicher Lookup-Overhead 4. GEO-BASED SHARDING ─────────────────── Shard EU: Europäische User Shard US: Amerikanische User Shard ASIA: Asiatische User ✅ Niedrige Latenz für User ✅ DSGVO-Compliance möglich ❌ Cross-Region Queries komplex
Shard Key wählen
Der SHARD KEY ist kritisch für die Performance! GUT ALS SHARD KEY: ✅ user_id - Queries meist pro User ✅ tenant_id - Multi-Tenant SaaS ✅ region - Geo-verteilte Daten ✅ date - Zeitreihen (mit Vorsicht) SCHLECHT ALS SHARD KEY: ❌ auto_increment id - Alle Writes auf einen Shard ❌ created_at - Hotspot auf aktuellem Shard ❌ status - Wenige Werte, ungleiche Verteilung ❌ häufig geänderter Wert - Resharding bei Updates BEISPIEL: E-Commerce // Shard Key: user_id // ✅ "Hole alle Bestellungen von User X" → 1 Shard // ❌ "Hole alle Bestellungen von heute" → Alle Shards // Shard Key: order_date // ✅ "Bestellungen von heute" → 1 Shard // ❌ "Bestellungen von User X" → Alle Shards // Lösung: Compound Shard Key oder denormalisieren
Implementierung (Konzept)
// Shard Router / Proxy
class ShardRouter {
private array $shards = [
'shard_1' => 'mysql://shard1.db.local',
'shard_2' => 'mysql://shard2.db.local',
'shard_3' => 'mysql://shard3.db.local',
'shard_4' => 'mysql://shard4.db.local',
];
public function getShard(int $userId): string {
// Hash-based Sharding
$shardIndex = $this->hashUserId($userId) % count($this->shards);
return array_keys($this->shards)[$shardIndex];
}
private function hashUserId(int $userId): int {
// Konsistentes Hashing für bessere Verteilung
return crc32((string) $userId);
}
public function getConnection(int $userId): PDO {
$shard = $this->getShard($userId);
return new PDO($this->shards[$shard]);
}
}
// Repository mit Sharding
class UserRepository {
public function __construct(private ShardRouter $router) {}
public function find(int $userId): ?User {
$conn = $this->router->getConnection($userId);
$stmt = $conn->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$userId]);
return $this->hydrate($stmt->fetch());
}
public function save(User $user): void {
$conn = $this->router->getConnection($user->id);
// INSERT/UPDATE auf dem richtigen Shard
}
// Cross-Shard Query (teuer!)
public function findAll(): array {
$results = [];
foreach ($this->router->getAllConnections() as $conn) {
$stmt = $conn->query('SELECT * FROM users');
$results = array_merge($results, $stmt->fetchAll());
}
return $results;
}
}
Herausforderungen
1. CROSS-SHARD QUERIES ❌ JOINs über Shards sind sehr teuer ❌ Aggregationen benötigen Scatter-Gather Lösung: - Daten denormalisieren - Reporting-Datenbank (alle Daten repliziert) - Materialized Views 2. CROSS-SHARD TRANSACTIONS ❌ ACID über mehrere Shards schwierig ❌ 2-Phase-Commit ist langsam Lösung: - Sagas statt Transactions - Eventual Consistency akzeptieren - Geschäftslogik anpassen 3. RESHARDING ❌ Daten umverteilen bei Shard-Änderung ist aufwendig ❌ Downtime oder komplexe Migration Lösung: - Konsistentes Hashing (nur Teil der Daten bewegen) - Von Anfang an genug Shards planen - Directory-based für Flexibilität 4. UNIQUE CONSTRAINTS ❌ Global einzigartige Werte schwer zu garantieren Lösung: - UUIDs statt Auto-Increment - Shard-ID in ID einbauen - Zentrale ID-Generierung (Twitter Snowflake)
Wann Sharding?
NICHT zu früh sharden! Erst wenn: ✅ Single-Server Limits erreicht (trotz Optimierung) ✅ Reads und Writes beide hoch ✅ Datenbank > 1TB und wächst schnell ✅ Team hat Erfahrung mit verteilten Systemen VORHER ausprobieren: 1. Query-Optimierung (Indexes!) 2. Read Replicas für Leselast 3. Caching (Redis) 4. Vertikales Scaling (mehr RAM, schnellere CPU) 5. Partitioning (innerhalb einer DB) "If you're not Facebook, you probably don't need sharding yet."
Sharding vs. Partitioning
PARTITIONING (innerhalb einer DB) ┌─────────────────────────────────┐ │ SINGLE DATABASE │ │ ┌─────────┬─────────┬───────┐ │ │ │ Part 1 │ Part 2 │Part 3 │ │ │ │ 2022 │ 2023 │ 2024 │ │ │ └─────────┴─────────┴───────┘ │ │ Gleicher Server, logisch │ │ getrennte Tabellen │ └─────────────────────────────────┘ SHARDING (mehrere DBs) ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Server 1 │ │ Server 2 │ │ Server 3 │ │ Shard 1 │ │ Shard 2 │ │ Shard 3 │ └──────────┘ └──────────┘ └──────────┘ Physisch getrennte Server → Partitioning zuerst, Sharding wenn nötig
Managed Sharding Lösungen
Statt selbst zu implementieren: 1. VITESS (für MySQL) - YouTube/Google nutzt es - Automatisches Sharding - MySQL-kompatibles Interface 2. CITUS (für PostgreSQL) - Distributed PostgreSQL - Transparentes Sharding - Als Extension verfügbar 3. MONGODB - Eingebautes Sharding - Config Server + Mongos Router - Automatisches Balancing 4. COCKROACHDB / TIDB - NewSQL Datenbanken - Automatisches Sharding - SQL-kompatibel, verteilt - ACID über Shards 5. CLOUD-MANAGED - AWS Aurora (Read Replicas) - Google Cloud Spanner (global verteilt) - Azure Cosmos DB (multi-model)
⚠️ Warnung:
Sharding ist komplex und erhöht die Betriebslast erheblich. Es sollte der letzte Ausweg sein, nicht die erste Lösung. Die meisten Anwendungen kommen mit einer gut optimierten einzelnen Datenbank + Read Replicas aus.