Skalierbare Datenspeicherung: SQL vs. NoSQL
Wählen Sie abhängig von Zugriffsmustern, Konsistenz- und Skalierbarkeitsanforderungen zwischen relationalen Datenbanken, Key-Value-Stores, Dokumentdatenbanken und Wide-Column-Stores.
Skalierbare Datenspeicherung: SQL vs. NoSQL ist eine kostenlose DSA Interview Prep-Lektion auf CoddyKit. Dies ist Lektion 2 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des DSA Interview Prep-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der DSA Interview Prep-Kurs umfasst insgesamt 4 Lektionen.
Die Wahl des Speichers ist eine Abwägung
Die Wahl eines Datenspeichers gehört zu den folgenreichsten Entscheidungen beim Systemdesign. Keine Datenbank ist universell die beste – jeder Typ ist für andere Zugriffsmuster, Konsistenzgarantien und Skalierungseigenschaften optimiert. Eine falsche Entscheidung in der Produktion führt zu monatelanger, aufwendiger Migrationsarbeit.
In Interviews prüfen Interviewer, ob Sie die grundlegenden Unterschiede verstehen und eine Speicher-Engine passend zu den Anforderungen eines Problems auswählen können. Die Frage lautet nie „Welche Lösung ist besser?“, sondern „Welche Lösung ist für diese konkrete Arbeitslast besser?“ Begründen Sie Ihre Wahl stets anhand konkreter Anforderungen.
# Storage decision matrix summary
factors = [
'Data structure (tabular, documents, key-value, graph, time-series)',
'Read vs write ratio (read-heavy, write-heavy, balanced)',
'Query patterns (point lookups, range scans, aggregations, joins)',
'Consistency requirements (ACID vs eventual consistency)',
'Scale requirements (single node, sharding, global distribution)',
'Latency requirements (milliseconds vs microseconds)',
'Team familiarity and operational complexity',
]
print('Key factors for storage selection:')
for f in factors:
print(f' - {f}')Stärken relationaler Datenbanken (SQL)
Relationale Datenbanken (PostgreSQL, MySQL, SQLite) speichern Daten in Tabellen mit festen Schemata und unterstützen ACID-Transaktionen – Atomarität, Konsistenz, Isolation und Dauerhaftigkeit. Sie eignen sich besonders für komplexe Abfragen mit Joins, Aggregationen und Filtern und sind daher ideal für strukturierte Daten mit klar definierten Beziehungen.
Wichtige Stärken: komplexe Abfragen über mehrere Tabellen mit SQL, Fremdschlüssel zur Sicherstellung der Datenintegrität, leistungsfähige Indizes (B-Tree, Hash, Volltext) sowie ein ausgereiftes Ökosystem mit Replikation und Backups. Verwenden Sie SQL, wenn Ihre Daten stark relational sind, Konsistenz entscheidend ist und die Abfragen komplex und vielfältig sind.
# SQL excels at: complex queries, joins, transactions
# Example: find top 5 products by revenue this month
sql_query = '''
SELECT p.name, SUM(oi.quantity * oi.price) AS revenue
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE o.created_at >= DATE_TRUNC('month', NOW())
GROUP BY p.id, p.name
ORDER BY revenue DESC
LIMIT 5;
'''
print('SQL shines for relational queries with JOINs:')
print(sql_query)
print('ACID guarantees example (transfer $100 between accounts):')
transfer_sql = '''
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- either both succeed or neither does
'''
print(transfer_sql)Herausforderungen bei der SQL-Skalierung
SQL-Datenbanken skalieren naturgemäß vertikal (größerer Server, mehr CPU/RAM), während die horizontale Skalierung komplex ist. Read-Replicas bewältigen leseintensive Arbeitslasten, indem Lesevorgänge an Replica-Knoten und Schreibvorgänge an den Primärknoten weitergeleitet werden. Der Schreibdurchsatz ist jedoch auf einen einzelnen Primärknoten begrenzt, sofern Sie kein Sharding einsetzen.
Sharding partitioniert Daten anhand eines Shard-Schlüssels (z. B. user_id mod N) auf mehrere DB-Instanzen. Dadurch lässt sich der Schreibdurchsatz skalieren, aber Joins und Transaktionen über mehrere Shards hinweg werden unmöglich – zwei der größten Stärken von SQL. Die meisten Webanwendungen stoßen bei etwa 5–10 TB Daten oder rund 100.000 Schreibvorgängen pro Sekunde an die Grenzen einer einzelnen SQL-Instanz.
# SQL scaling strategies
strategies = {
'Read replicas': {
'how': 'One primary (writes), multiple replicas (reads)',
'scales': 'Read throughput (10x+)',
'limit': 'Write throughput still bounded by single primary',
},
'Connection pooling (PgBouncer)': {
'how': 'Pool of persistent DB connections shared among app servers',
'scales': 'Connection count (PostgreSQL max ~500 connections)',
'limit': 'Does not increase query throughput',
},
'Horizontal sharding': {
'how': 'Partition rows by shard key across N database instances',
'scales': 'Both reads and writes (N×)',
'limit': 'Cross-shard joins and transactions broken; complex routing',
},
'CQRS': {
'how': 'Separate write model (SQL) from read model (denormalised/NoSQL)',
'scales': 'Optimise each path independently',
'limit': 'Eventual consistency between write and read models',
},
}
for strategy, info in strategies.items():
print(f'{strategy}:\n How: {info["how"]}\n Scales: {info["scales"]}\n Limit: {info["limit"]}\n')Schlüssel-Wert-Speicher: Redis und DynamoDB
Schlüssel-Wert-Speicher speichern Daten als Schlüssel- → Wert-Paare und bieten für Zugriffe über den Schlüssel Lese- und Schreibvorgänge in O(1). Sie verzichten auf flexible Abfragen zugunsten extremer Performance und horizontaler Skalierbarkeit. Redis (im Arbeitsspeicher) erreicht eine Latenz im Mikrosekundenbereich; DynamoDB (als verwalteter Dienst) erreicht eine Latenz im einstelligen Millisekundenbereich und skaliert automatisch auf jeden Durchsatz.
Verwenden Sie Schlüssel-Wert-Speicher für: Sitzungsspeicherung, Caching, Feature-Flags, Zuordnungen für URL-Shortener, Warenkörbe und Echtzeit-Ranglisten. Verwenden Sie sie nicht, wenn Sie komplexe Abfragen, Beziehungen zwischen Entitäten oder Ad-hoc-Filterung benötigen – Sie können nur über einen exakten Schlüssel suchen.
# Key-value store use cases
kv_operations = {
'GET key': 'O(1) point lookup — the core operation',
'SET key value': 'O(1) insert or update',
'DEL key': 'O(1) delete',
'EXPIRE key ttl': 'Set time-to-live; key auto-deleted after ttl seconds',
'INCR key': 'Atomic increment — useful for counters and rate limiting',
'LPUSH/LRANGE': 'List operations — useful for queues and recent-items feeds',
'ZADD/ZRANGE': 'Sorted set — leaderboards, rate limiting with sliding window',
}
print('Redis operation set:')
for op, desc in kv_operations.items():
print(f' {op:25s}: {desc}')
print('\nDynamoDB vs Redis:')
print(' Redis: microsecond latency, in-memory, needs persistence config')
print(' DynamoDB: single-digit ms, managed, auto-scaling, durable by default')Dokumentenspeicher: MongoDB
Dokumentenspeicher (MongoDB, Couchbase) speichern Daten als JSON-ähnliche Dokumente und ermöglichen flexible Schemata – Felder können sich zwischen Dokumenten in derselben Collection unterscheiden. Sie unterstützen Indizes auf beliebigen Feldern sowie relativ umfangreiche Abfragen (Filter, Projektionen, Aggregationen), wobei Transaktionen über mehrere Dokumente hinweg eingeschränkter sind als bei SQL.
Dokumentenspeicher eignen sich für Anwendungen, bei denen: die Datenstruktur je Entität variiert (Benutzerprofile mit unterschiedlichen Attributen), häufige Änderungen am Schema zu erwarten sind (Start-ups ändern ihre Datenmodelle regelmäßig) oder die Zugriffsmuster hauptsächlich das Abrufen vollständiger Entitäten statt Joins über Tabellen umfassen.
# MongoDB document example
user_doc = {
'_id': 'user123',
'name': 'Alice',
'email': 'alice@example.com',
'preferences': {
'theme': 'dark',
'language': 'en',
'notifications': ['email', 'push']
},
'addresses': [
{'type': 'home', 'city': 'Berlin', 'country': 'DE'},
{'type': 'work', 'city': 'Munich', 'country': 'DE'}
],
'subscription_tier': 'pro',
# Note: not all users have all fields -- flexible schema!
}
import json
print('Document structure (flexible schema):')
print(json.dumps(user_doc, indent=2))
print('\nDocument store strengths:')
print(' - Nested/array fields without joins')
print(' - Flexible schema (different fields per document)')
print(' - Scales horizontally by sharding on _id')Wide-Column-Speicher: Cassandra
Wide-Column-Speicher (Apache Cassandra, HBase) speichern Daten in Zeilen und Spalten, erlauben aber für jede Zeile eine unterschiedliche Menge an Spalten. Sie sind für enormen Schreibdurchsatz ausgelegt, der über viele Knoten verteilt wird, und verwenden standardmäßig Eventual Consistency. Cassandra erreicht eine lineare Skalierung des Schreibdurchsatzes – eine Verdopplung der Knoten verdoppelt den Schreibdurchsatz.
Die Abwägung: Abfragen müssen um den Partition Key herum entworfen werden. Sie können nicht effizient nach beliebigen Spalten filtern oder sortieren – Sie müssen zuerst das Abfragemuster definieren und anschließend die Tabelle passend dazu entwerfen. Das ist das Gegenteil des SQL-Ansatzes „Daten entwerfen und anschließend beliebige Abfragen schreiben“.
# Cassandra table design for time-series events
# Design around the query: 'give me all events for user X, most recent first'
cassandra_table = '''
CREATE TABLE user_events (
user_id UUID,
event_time TIMESTAMP,
event_type TEXT,
metadata MAP<TEXT, TEXT>,
PRIMARY KEY (user_id, event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);
-- Query (matches partition key exactly):
SELECT * FROM user_events WHERE user_id = ? LIMIT 100;
'''
print('Cassandra wide-column design:')
print(cassandra_table)
print('Properties:')
print(' - user_id = partition key (all rows for one user on same node)')
print(' - event_time = clustering key (sorted within partition)')
print(' - Very fast writes: append-only, no locking')
print(' - Cannot query by event_type alone (no partition key)')CAP-Theorem: Konsistenz, Verfügbarkeit und Partitionstoleranz
Das CAP-Theorem besagt, dass ein verteiltes System höchstens zwei der folgenden Eigenschaften garantieren kann: Konsistenz (jeder Lesevorgang gibt den neuesten Schreibvorgang zurück), Verfügbarkeit (jede Anfrage erhält eine Antwort ohne Fehler) und Partitionstoleranz (das System arbeitet trotz Netzwerkpartitionen weiter).
Da Netzwerkpartitionen in verteilten Systemen immer auftreten, besteht die tatsächliche Wahl zwischen CP (Konsistenz wird priorisiert, während einer Partition können Anfragen abgewiesen werden) und AP (Verfügbarkeit wird priorisiert, möglicherweise werden veraltete Daten zurückgegeben). SQL-Datenbanken sind im Allgemeinen CP; Cassandra und DynamoDB sind AP (standardmäßig mit Eventual Consistency). Redis Cluster ist CP.
# CAP theorem applied to common databases
databases = {
'PostgreSQL (single node)': {'C': True, 'A': True, 'P': False, 'note': 'Not distributed; CA'},
'PostgreSQL (multi-AZ)': {'C': True, 'A': False, 'P': True, 'note': 'CP: primary fails over, brief downtime'},
'MySQL Cluster': {'C': True, 'A': False, 'P': True, 'note': 'CP'},
'Cassandra': {'C': False, 'A': True, 'P': True, 'note': 'AP: eventual consistency default'},
'DynamoDB (default)': {'C': False, 'A': True, 'P': True, 'note': 'AP: eventual consistency'},
'DynamoDB (strong read)': {'C': True, 'A': False, 'P': True, 'note': 'CP: strongly consistent reads'},
'Redis Cluster': {'C': True, 'A': False, 'P': True, 'note': 'CP'},
'MongoDB (default)': {'C': True, 'A': False, 'P': True, 'note': 'CP: reads from primary'},
}
for db, caps in databases.items():
c_str = 'C' if caps['C'] else '-'
a_str = 'A' if caps['A'] else '-'
p_str = 'P' if caps['P'] else '-'
print(f'{db:35s} [{c_str}{a_str}{p_str}] {caps["note"]}')Ein Framework zur Wahl des Speichers
Ein praktisches Framework für die Wahl des Speichers in Interviews:
- Benötigen Sie ACID-Transaktionen? → Relationale DB (PostgreSQL, MySQL)
- Benötigen Sie Suchvorgänge im Submillisekundenbereich oder Caching? → Schlüssel-Wert-Speicher (Redis)
- Benötigen Sie ein flexibles Schema oder dokumentenorientierte Daten? → Dokumentenspeicher (MongoDB)
- Benötigen Sie enormen Schreibdurchsatz (>100K/Sek.) mit Zeitreihen- oder Ereignisdaten? → Wide-Column-Speicher (Cassandra)
- Benötigen Sie globale Verteilung mit verwalteter Skalierung? → DynamoDB oder Cosmos DB
- Benötigen Sie Graphdurchläufe? → Graphdatenbank (Neo4j)
# Decision tree in code form
def choose_storage(needs_acid, high_write_throughput, flexible_schema,
sub_ms_latency, graph_queries, global_scale):
if sub_ms_latency:
return 'Redis (in-memory key-value)'
if graph_queries:
return 'Neo4j (graph database)'
if needs_acid:
return 'PostgreSQL / MySQL (relational)'
if high_write_throughput and not flexible_schema:
return 'Cassandra (wide-column, write-optimised)'
if flexible_schema:
return 'MongoDB (document store)'
if global_scale:
return 'DynamoDB or Cosmos DB (managed global KV/document)'
return 'PostgreSQL (safe default for most web apps)'
# Example scenarios
scenarios = [
{'needs_acid': True, 'high_write_throughput': False, 'flexible_schema': False,
'sub_ms_latency': False, 'graph_queries': False, 'global_scale': False},
{'needs_acid': False, 'high_write_throughput': True, 'flexible_schema': False,
'sub_ms_latency': False, 'graph_queries': False, 'global_scale': False},
{'needs_acid': False, 'high_write_throughput': False, 'flexible_schema': False,
'sub_ms_latency': True, 'graph_queries': False, 'global_scale': False},
]
for s in scenarios:
print(f'{choose_storage(**s)}')Polyglot Persistence: Verwendung mehrerer Speicher
Produktionssysteme verwenden nur selten eine einzige Datenbank für alle Zwecke. Polyglot Persistence bedeutet, für jeden Teil des Systems die passende Speichertechnologie einzusetzen. Eine typische Webanwendung könnte PostgreSQL für maßgebliche Benutzer- und Bestelldaten, Redis für Sitzungsspeicherung und Caching, Elasticsearch für die Volltextsuche, S3 für die Dateispeicherung und Cassandra für Ereignisprotokolle und Analysen verwenden.
Die Abwägung: Mit jedem Datenbanktyp steigt die betriebliche Komplexität. Das Team muss mehrere Systeme warten, überwachen und sichern. Begründung für das Design: Die bei einer passenden Zuordnung jeder Arbeitslast zur richtigen Speichertechnologie erzielten Performance- und Skalierungsvorteile überwiegen bei großen Systemen die betrieblichen Kosten.
# Polyglot persistence in an e-commerce system
components = {
'User accounts, orders, payments': {
'storage': 'PostgreSQL',
'reason': 'ACID transactions (payment integrity), complex queries',
},
'Product catalogue': {
'storage': 'MongoDB or PostgreSQL with JSONB',
'reason': 'Flexible product attributes vary by category',
},
'Session tokens': {
'storage': 'Redis (with TTL)',
'reason': 'O(1) lookup, automatic expiry, high throughput',
},
'Product search': {
'storage': 'Elasticsearch',
'reason': 'Full-text search, faceted filtering, relevance scoring',
},
'Activity/event log': {
'storage': 'Cassandra or Kafka + S3',
'reason': 'High write throughput, append-only, time-series queries',
},
'Product images / videos': {
'storage': 'S3 + CloudFront CDN',
'reason': 'Cheap object storage, global distribution via CDN',
},
}
for component, info in components.items():
print(f'{component}:\n {info["storage"]}: {info["reason"]}\n')Indexierungsstrategie für verschiedene Speichertypen
Alle Speichersysteme verwenden Indizes, um Lesevorgänge zu beschleunigen – auf Kosten langsamerer Schreibvorgänge und zusätzlichen Speicherbedarfs. Für das Systemdesign ist es entscheidend, die Indexierung über verschiedene Speichertypen hinweg zu verstehen:
- SQL: B-Tree-Index auf jeder Spalte; zusammengesetzte Indizes für Abfragen über mehrere Spalten; abdeckende Indizes, um Tabellenzugriffe zu vermeiden
- MongoDB: Index auf jedem Feld; zusammengesetzte Indizes; TTL-Indizes für den automatischen Ablauf
- Cassandra: Nativ werden nur Partition Key und Clustering-Spalten indexiert; sekundäre Indizes sind aufwendig
- Redis: Sortierte Mengen als Indizes für Bereichsabfragen; für Schlüssel-Wert-Zugriffe sind keine herkömmlichen Indizes erforderlich
# Indexing examples across storage types
# PostgreSQL: B-tree composite index
postgres_index = '''
CREATE INDEX idx_orders_user_created
ON orders(user_id, created_at DESC);
-- Optimises: SELECT * FROM orders WHERE user_id=? ORDER BY created_at DESC
'''
# MongoDB: compound index
mongo_index = '''
db.products.createIndex({ category: 1, price: -1 })
// Optimises: db.products.find({category:'Electronics'}).sort({price:-1})
'''
# Cassandra: cluster key ordering (built into table design)
cassandra_index = '''
-- No separate index needed; clustering key IS the index:
PRIMARY KEY (user_id, event_time) WITH CLUSTERING ORDER BY (event_time DESC)
'''
print('PostgreSQL:', postgres_index)
print('MongoDB:', mongo_index)
print('Cassandra:', cassandra_index)SQL vs. NoSQL im Interview: Was Sie sagen sollten
Wenn Sie in einem System-Design-Interview gefragt werden „SQL oder NoSQL?“, geben Sie niemals eine Antwort mit nur einem Wort. Verwenden Sie stattdessen diese Struktur:
- Nennen Sie die Arbeitslast: „Die Arbeitslast ist leseintensiv und erfordert komplexe Filterung, daher …“
- Nennen Sie die Anforderung: „Für Finanztransaktionen benötigen wir starke Konsistenz, daher …“
- Nennen Sie die Wahl: „Ich würde PostgreSQL mit Read-Replicas verwenden“
- Nennen Sie die Abwägung: „Die Abwägung besteht darin, dass die horizontale Skalierung des Schreibdurchsatzes Sharding erfordert, was zusätzliche Komplexität verursacht“
- Nennen Sie eine Alternative: „Bei höherem Schreibaufkommen könnten wir DynamoDB in Betracht ziehen“
# Sample answer structure for 'SQL or NoSQL?'
def answer_storage_question(workload, consistency_need, scale):
print(f'Workload: {workload}')
print(f'Consistency: {consistency_need}')
print(f'Scale: {scale}')
print()
if 'financial' in workload.lower() or consistency_need == 'strong':
choice = 'PostgreSQL (ACID, strong consistency)'
tradeoff = 'Horizontal write scaling requires sharding'
alternative = 'Google Spanner for global transactions'
elif 'event' in workload.lower() or 'log' in workload.lower():
choice = 'Cassandra (high write throughput, time-series)'
tradeoff = 'Eventual consistency; queries limited to partition key'
alternative = 'Kafka + S3 for long-term event archival'
else:
choice = 'DynamoDB (managed, auto-scale, low latency)'
tradeoff = 'Limited query flexibility; cross-item transactions limited'
alternative = 'PostgreSQL if complex queries emerge'
print(f'Choice: {choice}\nTrade-off: {tradeoff}\nAlternative: {alternative}')
answer_storage_question('Social media feed', 'eventual', '10M users')Kurzer Test
Testen Sie Ihr Verständnis der Konzepte aus Data Structures & Algorithms — Coding Interview Prep in dieser Lektion.
Zusammenfassung der Lektion
In dieser Lektion haben Sie gelernt: SQL-Datenbanken bieten ACID-Transaktionen und komplexe Abfragen, skalieren den Schreibdurchsatz jedoch nur mit Schwierigkeiten, während NoSQL-Datenbanken Konsistenz oder Flexibilität bei Abfragen zugunsten extremer Skalierbarkeit und Verfügbarkeit opfern, das CAP-Theorem zwingt verteilte Systeme dazu, sich bei Netzwerkpartitionen zwischen Konsistenz und Verfügbarkeit zu entscheiden und Produktionssysteme verwenden typischerweise Polyglot Persistence – jede Arbeitslast wird der passenden Speicher-Engine zugeordnet. Als Nächstes sehen wir uns Caching-Schichten, CDNs und Load Balancing an, um leseintensive Systeme noch weiter zu skalieren.
Häufig gestellte Fragen
Ist die Lektion „Skalierbare Datenspeicherung: SQL vs. NoSQL“ kostenlos?
Ja — der vollständige Text von „Skalierbare Datenspeicherung: SQL vs. NoSQL“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des DSA Interview Prep-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der DSA Interview Prep-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Skalierbare Datenspeicherung: SQL vs. NoSQL“?
Wählen Sie abhängig von Zugriffsmustern, Konsistenz- und Skalierbarkeitsanforderungen zwischen relationalen Datenbanken, Key-Value-Stores, Dokumentdatenbanken und Wide-Column-Stores. Du übst DSA Interview Prep mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um DSA Interview Prep zu starten?
Keine Vorkenntnisse erforderlich. DSA Interview Prep auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 2 von 4.
Wie lange dauert die Lektion „Skalierbare Datenspeicherung: SQL vs. NoSQL“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser DSA Interview Prep-Lektion Code schreiben und ausführen?
Ja. Jede DSA Interview Prep-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Das Framework für System-Design-Interviews
- Skalierbare Datenspeicherung: SQL vs. NoSQL
- Caching, CDNs und Load Balancing
- Rate Limiter und Twitter-Feed entwerfen