ElastiCache Redis einsetzen: Wann ein Caching-Layer deine RDS-Datenbank entlastet

Du hast die Abfragen optimiert, Indizes gesetzt, Read Replicas hinzugefügt — und der RDS-Endpunkt ist immer noch der Engpass. Das passiert fast immer dann, wenn dieselben Zeilen hunderte Male pro Sekunde gelesen werden und jede Anfrage trotzdem den vollen Datenbankpfad durchläuft. Ein ElastiCache Redis-Layer löst genau dieses Problem, indem er häufig gelesene Ergebnisse im Arbeitsspeicher hält und RDS damit aus der kritischen Anfragekette herausnimmt.

TL;DR: ElastiCache Redis als Caching-Layer vor RDS

AspektOhne CacheMit ElastiCache Redis
LesepfadApp → RDS (Disk/Buffer Pool)App → Redis (RAM) → RDS nur bei Cache Miss
Latenz (typisch)Einige Millisekunden bis Dutzende msUnter 1 ms bei Cache Hit
RDS-Last bei hohem TrafficLinear steigendDeutlich reduziert bei hoher Hit-Rate
DatenkonsistenzImmer aktuellAbhängig von TTL und Invalidierungsstrategie
Operativer AufwandGeringMittel (Cache-Strategie, TTL, Invalidierung)

Wie ElastiCache Redis als Caching-Layer funktioniert

Redis ist ein In-Memory-Datenspeicher. ElastiCache stellt Redis als verwalteten Dienst bereit — du bekommst Cluster-Endpunkte, automatisches Failover bei aktiviertem Cluster-Modus oder Multi-AZ mit Replikation, und kein Betriebssystem-Management. Der entscheidende Punkt für das RDS-Entlastungsszenario: Redis hält Daten ausschließlich im RAM, was Lesezugriffe um Größenordnungen schneller macht als jede relationale Datenbank mit Disk-I/O.

Das grundlegende Muster heißt Cache-Aside (auch Lazy Loading genannt). Die Applikation prüft zuerst Redis. Liegt der Schlüssel vor (Cache Hit), wird das Ergebnis direkt zurückgegeben. Liegt er nicht vor (Cache Miss), fragt die App RDS ab, schreibt das Ergebnis in Redis mit einer TTL, und gibt es zurück. RDS wird nur noch bei Cache Misses belastet.

graph LR App["Applikation"] Redis["ElastiCache Redis
(In-Memory)"] RDS["Amazon RDS
(Relationale DB)"] App -->|"1. GET cache_key"| Redis Redis -->|"Cache Hit: Daten direkt"| App Redis -->|"Cache Miss"| App App -->|"2. SQL-Abfrage (nur bei Miss)"| RDS RDS -->|"3. Ergebnis"| App App -->|"4. SETEX cache_key TTL"| Redis
  1. Cache Hit: Die Applikation fragt Redis ab und erhält das Ergebnis direkt aus dem RAM — RDS wird nicht kontaktiert.
  2. Cache Miss: Redis kennt den Schlüssel nicht. Die Applikation fragt RDS ab, schreibt das Ergebnis mit TTL in Redis und gibt es zurück.
  3. TTL-Ablauf: Nach Ablauf der TTL wird der Schlüssel aus Redis entfernt. Der nächste Zugriff erzeugt einen Cache Miss und füllt den Cache neu.
  4. Schreibpfad: Schreiboperationen gehen direkt an RDS. Je nach Strategie wird der betroffene Cache-Schlüssel entweder sofort invalidiert oder läuft über die TTL ab.

Wann ElastiCache Redis tatsächlich hilft — und wann nicht

Redis löst ein spezifisches Problem: hohe Lesefrequenz auf Daten, die sich selten ändern. Bevor du den Caching-Layer einbaust, lohnt sich eine ehrliche Einschätzung deines Workloads.

Gute Kandidaten für Redis-Caching:

  • Produktkataloge, Konfigurationsdaten, Referenztabellen — lesen viele, schreiben wenige
  • Session-Daten und Authentifizierungstoken — kurze TTL, sehr hohe Zugriffsfrequenz
  • Aggregierte Auswertungen (z. B. Dashboard-Metriken), die teuer zu berechnen sind
  • API-Antworten externer Dienste, die gecacht werden dürfen

Schlechte Kandidaten:

  • Transaktionale Daten, bei denen jeder Lesevorgang den aktuellsten Stand erfordert
  • Daten mit sehr hoher Schreibrate — der Cache wird ständig invalidiert, die Hit-Rate bleibt niedrig
  • Einmalige oder seltene Abfragen — der Overhead überwiegt den Nutzen
Redis ist kein universeller Datenbankersatz. Es ist ein Beschleuniger für Lesemuster, die sich wiederholen. Wenn deine Abfragen jeden Aufruf ein anderes Ergebnis liefern, hilft kein Cache der Welt.

ElastiCache Redis-Cluster einrichten: Schritt-für-Schritt

Schritt 1: Subnet-Gruppe erstellen

ElastiCache-Cluster müssen in einer Subnet-Gruppe platziert werden, die deine privaten Subnetze referenziert. Der Cluster sollte niemals öffentlich erreichbar sein — er kommuniziert ausschließlich mit deinen Applikationsinstanzen innerhalb der VPC.

aws elasticache create-cache-subnet-group \
  --cache-subnet-group-name my-redis-subnet-group \
  --cache-subnet-group-description "Redis Subnet Group fuer App-Layer" \
  --subnet-ids subnet-0abc123456789def0 subnet-0def987654321abc0 \
  --region us-east-1

Schritt 2: Security Group konfigurieren

Redis lauscht standardmäßig auf Port 6379. Die Security Group des Redis-Clusters darf eingehenden Traffic auf diesem Port nur von der Security Group deiner Applikationsinstanzen erlauben — nicht aus dem gesamten VPC-CIDR. Das begrenzt die Angriffsfläche erheblich.

aws ec2 create-security-group \
  --group-name redis-sg \
  --description "Security Group fuer ElastiCache Redis" \
  --vpc-id vpc-0123456789abcdef0 \
  --region us-east-1
aws ec2 authorize-security-group-ingress \
  --group-id sg-0redis1234567890a \
  --protocol tcp \
  --port 6379 \
  --source-group sg-0app1234567890b \
  --region us-east-1

Schritt 3: Replication Group erstellen (Multi-AZ)

Für Produktionsumgebungen empfiehlt sich eine Replication Group mit automatischem Failover. Das bedeutet: ein primärer Knoten für Schreiboperationen und mindestens ein Read Replica in einer anderen Availability Zone. Bei einem Ausfall des primären Knotens übernimmt ElastiCache automatisch die Promotion eines Replicas.

🔽 CLI-Befehl: Replication Group erstellen (aufklappen)
aws elasticache create-replication-group \
  --replication-group-id my-redis-cluster \
  --replication-group-description "Redis Cache fuer RDS-Entlastung" \
  --cache-node-type cache.r7g.large \
  --engine redis \
  --engine-version 7.0 \
  --num-cache-clusters 2 \
  --cache-subnet-group-name my-redis-subnet-group \
  --security-group-ids sg-0redis1234567890a \
  --automatic-failover-enabled \
  --multi-az-enabled \
  --at-rest-encryption-enabled \
  --transit-encryption-enabled \
  --region us-east-1

Der Parameter --num-cache-clusters 2 erzeugt einen primären Knoten und ein Replica. --at-rest-encryption-enabled und --transit-encryption-enabled sollten in Produktionsumgebungen immer gesetzt sein.

Schritt 4: Cluster-Endpunkt abfragen

Nach der Erstellung benötigst du den primären Endpunkt für Schreiboperationen und den Reader-Endpunkt für Lesezugriffe. ElastiCache stellt beide bereit.

aws elasticache describe-replication-groups \
  --replication-group-id my-redis-cluster \
  --query 'ReplicationGroups[0].NodeGroups[0].PrimaryEndpoint' \
  --region us-east-1

Schritt 5: Cache-Aside-Muster in der Applikation implementieren

Das Muster ist unabhängig von der Programmiersprache immer gleich. Hier ein Python-Beispiel mit redis-py, das den typischen Ablauf zeigt:

🔽 Python-Beispiel: Cache-Aside mit Redis und RDS (aufklappen)
import redis
import json
import psycopg2

redis_client = redis.Redis(
    host='my-redis-cluster.xxxxx.ng.0001.use1.cache.amazonaws.com',
    port=6379,
    ssl=True,
    decode_responses=True
)

def get_product(product_id: str) -> dict:
    cache_key = f'product:{product_id}'

    # 1. Redis pruefen
    cached = redis_client.get(cache_key)
    if cached:
        return json.loads(cached)

    # 2. Cache Miss: RDS abfragen
    conn = psycopg2.connect(dsn='...')
    cursor = conn.cursor()
    cursor.execute('SELECT id, name, price FROM products WHERE id = %s', (product_id,))
    row = cursor.fetchone()
    cursor.close()
    conn.close()

    if row is None:
        return None

    product = {'id': row[0], 'name': row[1], 'price': str(row[2])}

    # 3. Ergebnis in Redis schreiben, TTL 300 Sekunden
    redis_client.setex(cache_key, 300, json.dumps(product))

    return product

Cache-Invalidierung: Das eigentliche Problem

Das Befüllen des Caches ist trivial. Die Invalidierung ist es nicht. Wenn ein Produkt in RDS aktualisiert wird und der Cache-Schlüssel noch 290 Sekunden TTL hat, liefert deine API veraltete Daten — und du wirst es erst merken, wenn ein Nutzer sich beschwert.

Es gibt zwei verbreitete Ansätze:

TTL-basierte Invalidierung: Der Schlüssel läuft automatisch ab. Einfach zu implementieren, akzeptiert aber ein Fenster veralteter Daten. Für Produktkataloge oder Konfigurationsdaten oft ausreichend.

Explizite Invalidierung beim Schreiben: Jede Schreiboperation auf RDS löscht sofort den betroffenen Cache-Schlüssel. Die nächste Leseanfrage erzeugt einen Cache Miss und füllt den Cache mit frischen Daten.

# Beim Update in RDS: Cache-Schluessel sofort loeschen
def update_product(product_id: str, new_data: dict):
    # RDS-Update
    conn = psycopg2.connect(dsn='...')
    cursor = conn.cursor()
    cursor.execute(
        'UPDATE products SET name = %s, price = %s WHERE id = %s',
        (new_data['name'], new_data['price'], product_id)
    )
    conn.commit()
    cursor.close()
    conn.close()

    # Cache-Schluessel invalidieren
    cache_key = f'product:{product_id}'
    redis_client.delete(cache_key)

Die explizite Invalidierung klingt sauber, hat aber eine Falle: Wenn der RDS-Commit erfolgreich ist, aber das redis_client.delete() durch einen Netzwerkfehler fehlschlägt, bleibt ein veralteter Schlüssel im Cache. Eine kurze TTL als Sicherheitsnetz ist deshalb auch bei expliziter Invalidierung sinnvoll.

sequenceDiagram participant App as Applikation participant RDS as Amazon RDS participant Redis as ElastiCache Redis App->>RDS: UPDATE products SET ... RDS-->>App: Commit OK App->>Redis: DEL product:42 Redis-->>App: OK Note over Redis: Schluessel entfernt App->>Redis: GET product:42 Redis-->>App: Cache Miss App->>RDS: SELECT * FROM products WHERE id=42 RDS-->>App: Aktuelle Daten App->>Redis: SETEX product:42 300 ... Redis-->>App: OK (Cache neu befuellt)
  1. Schreibpfad: Updates gehen direkt an RDS. Danach wird der betroffene Cache-Schlüssel in Redis gelöscht.
  2. Nächster Lesezugriff: Cache Miss — Redis kennt den Schlüssel nicht mehr. RDS wird abgefragt, frische Daten werden in Redis geschrieben.
  3. TTL als Fallback: Falls die Invalidierung fehlschlägt, sorgt die TTL dafür, dass veraltete Daten nicht unbegrenzt im Cache verbleiben.

Fehldiagnose aus der Praxis: Wenn Redis die Last nicht reduziert

Ein typisches Szenario: Redis ist eingebaut, die RDS-CPU bleibt trotzdem hoch. Der erste Verdacht ist meistens die Hit-Rate — aber die liegt laut CloudWatch bei 95 %. Also woran liegt es?

In einem konkreten Fall war die Ursache eine Kombination aus zwei Dingen: Die Applikation verwendete Cache-Schlüssel, die eine User-ID enthielten (product:42:user:789), obwohl die Produktdaten gar nicht nutzerspezifisch waren. Jeder Nutzer erzeugte seinen eigenen Cache-Schlüssel für dasselbe Produkt. Die Hit-Rate war hoch, weil wiederkehrende Nutzer ihren eigenen Schlüssel trafen — aber die Gesamtzahl der Schlüssel im Cache explodierte, und neue Nutzer erzeugten ständig Cache Misses auf RDS.

Die Lösung war trivial: Cache-Schlüssel auf die reine Ressourcen-ID reduzieren (product:42). Die RDS-Last fiel innerhalb von Minuten auf ein Bruchteil des vorherigen Wertes.

Bevor du die Redis-Konfiguration oder den Instanztyp änderst: Prüfe zuerst die Schlüsselstruktur. Ein falsch konstruierter Cache-Schlüssel macht den besten Cluster wirkungslos.

IAM und Sicherheit für ElastiCache Redis

ElastiCache Redis unterstützt Redis AUTH (passwortbasierte Authentifizierung) sowie seit Redis 6 auch Role-Based Access Control (RBAC) über ElastiCache-Benutzer und Benutzergruppen. Für Produktionsumgebungen sollte Transit Encryption (TLS) aktiviert sein.

Die IAM-Berechtigungen für die Applikationsrolle, die den Cluster verwaltet, sollten dem Least-Privilege-Prinzip folgen:

🔽 IAM-Policy: ElastiCache-Zugriff für Applikationsrolle (aufklappen)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ElastiCacheDescribe",
      "Effect": "Allow",
      "Action": [
        "elasticache:DescribeReplicationGroups",
        "elasticache:DescribeCacheClusters"
      ],
      "Resource": "*"
    }
  ]
}

Hinweis: Die eigentliche Verbindung zum Redis-Cluster erfolgt über das Redis-Protokoll auf Netzwerkebene (TCP 6379), nicht über IAM-Aktionen. IAM-Berechtigungen steuern die Verwaltungsebene (ElastiCache-API), nicht die Datenebene. Die Zugriffskontrolle auf Datenebene erfolgt über Redis AUTH oder RBAC in Kombination mit Security Groups.

Monitoring: Welche Metriken wirklich wichtig sind

ElastiCache publiziert Metriken nach CloudWatch. Die relevantesten für das RDS-Entlastungsszenario:

MetrikWas sie zeigtHandlungsbedarf
CacheHitsAnzahl erfolgreicher Cache-TrefferNiedrige Rate → Schlüsselstrategie prüfen
CacheMissesAnzahl Cache MissesHohe Rate → TTL oder Schlüsselstruktur anpassen
CurrConnectionsAktive VerbindungenUnerwarteter Anstieg → Connection Pooling prüfen
EvictionsAus dem Cache verdrängte SchlüsselHoher Wert → Instanzgröße (mehr RAM) prüfen
DatabaseMemoryUsagePercentageRAM-Auslastung des ClustersÜber 80 % → Skalierung einplanen
ReplicationLagReplikationsverzögerung zum ReplicaAnhaltend hoch → Netzwerk oder Schreiblast prüfen
aws cloudwatch get-metric-statistics \
  --namespace AWS/ElastiCache \
  --metric-name CacheHits \
  --dimensions Name=CacheClusterId,Value=my-redis-cluster-0001-001 \
  --start-time 2024-01-15T10:00:00Z \
  --end-time 2024-01-15T11:00:00Z \
  --period 300 \
  --statistics Sum \
  --region us-east-1

Wann ElastiCache Redis als Caching-Layer nicht ausreicht

Redis löst das Leseproblem — aber wenn deine RDS-Last primär durch Schreiboperationen oder komplexe Joins entsteht, hilft kein Cache. In diesem Fall sind Read Replicas, Query-Optimierung oder eine Architekturüberarbeitung die richtigen Hebel.

Außerdem: Redis ist ein flüchtiger Speicher. Bei einem Neustart ohne Persistenz-Konfiguration (RDB oder AOF) ist der Cache leer. Das ist kein Fehler — es ist das erwartete Verhalten. Deine Applikation muss damit umgehen können, dass der Cache nach einem Neustart kalt ist und die ersten Anfragen alle auf RDS treffen (sogenannter Cold-Start-Effekt).

Fazit und nächste Schritte mit ElastiCache Redis

ElastiCache Redis ist das richtige Werkzeug, wenn dieselben Daten häufig gelesen werden und eine kurze Veraltungstoleranz akzeptabel ist. Die Implementierung des Cache-Aside-Musters ist überschaubar — die eigentliche Arbeit liegt in der Schlüsselstrategie und der Invalidierungslogik.

Empfohlene nächste Schritte:

  • Identifiziere die Top-5-Abfragen nach Ausführungshäufigkeit in RDS Performance Insights
  • Prüfe, ob die zurückgegebenen Daten nutzerneutral und cachebar sind
  • Starte mit einer konservativen TTL (60–300 Sekunden) und beobachte die Hit-Rate
  • Richte CloudWatch-Alarme für Evictions und DatabaseMemoryUsagePercentage ein
  • Dokumentiere die Invalidierungsstrategie explizit — sie ist der häufigste Fehlerort

Weiterführende AWS-Dokumentation: ElastiCache for Redis User Guide und ElastiCache Best Practices.

Glossar

BegriffBedeutung
Cache-Aside (Lazy Loading)Muster, bei dem die Applikation den Cache selbst befüllt — nur bei einem Cache Miss wird die Datenbank abgefragt.
Cache Hit / Cache MissHit: gesuchter Schlüssel ist im Cache vorhanden. Miss: Schlüssel fehlt, Datenbank wird abgefragt.
TTL (Time to Live)Ablaufzeit eines Cache-Schlüssels in Sekunden. Nach Ablauf wird der Schlüssel automatisch gelöscht.
EvictionAutomatisches Entfernen von Schlüsseln durch Redis, wenn der verfügbare RAM erschöpft ist.
Replication GroupElastiCache-Ressource, die einen primären Knoten und optionale Read Replicas mit automatischem Failover verwaltet.

Kommentare

Beliebte Posts aus diesem Blog

EC2 ohne Internetzugang im eigenen VPC – Internet Gateway und Route Table korrekt einrichten

LSI vs. GSI in DynamoDB: Den richtigen Sekundärindex wählen