🎉 Das neue Waldheim Customer Portal ist live – Zentrale Verwaltung für alle unsere Produkte!
Wir stellen vor: Das Customer Portal – Ihre zentrale Plattform zur Verwaltung von Lizenzen, Abonnements und Support für Ecodrive, Socialverse und Silvacore. Mit vollständiger Stripe-Integration, Multi-User-System und 2FA-Sicherheit. 

Go und Redis: Meine Muster für hochperformantes Caching in Microservices


Ich stelle meine bewährten Caching-Strategien mit Go und Redis vor, die die Antwortzeiten von APIs drastisch reduzieren.

Go und Redis: Das Power-Duo für blitzschnelle Microservices

In der Welt der Microservices ist die Antwortzeit (Latenz) ein entscheidendes Qualitätsmerkmal. Jede Millisekunde zählt. Oft ist der langsamste Teil einer Anfrage der Zugriff auf eine persistente Datenbank wie PostgreSQL. Selbst eine gut optimierte Datenbankabfrage ist um Größenordnungen langsamer als der Zugriff auf den Arbeitsspeicher. Genau hier kommt Caching ins Spiel: die Kunst, häufig abgerufene Daten in einem schnellen Zwischenspeicher vorzuhalten, um den langsameren Primärspeicher zu entlasten.

Für meine Go-basierten Microservices gibt es ein unschlagbares Power-Duo für diese Aufgabe: Go und Redis. Go bietet mit seiner Nebenläufigkeit die perfekte Basis, um Anfragen effizient zu verarbeiten, während Redis als extrem schneller In-Memory-Datenspeicher den idealen Cache darstellt. In diesem Beitrag stelle ich meine bewährten Caching-Muster vor, mit denen ich die Antwortzeiten meiner APIs drastisch reduziere und die Last auf meinen Datenbanken minimiere.

Warum Redis der perfekte Cache für Go ist

  • Extreme Geschwindigkeit: Redis hält alle Daten im RAM, was Lese- und Schreibzugriffe im Mikrosekundenbereich ermöglicht.
  • Vielseitige Datenstrukturen: Redis ist mehr als nur ein einfacher Key-Value-Store. Es bietet Hashes, Listen, Sets und mehr, die für komplexe Caching-Szenarien genutzt werden können.
  • Time-to-Live (TTL): Die Fähigkeit, Schlüssel automatisch nach einer bestimmten Zeit verfallen zu lassen, ist die Grundlage für eine effektive Cache-Invalidierung.
  • Skalierbarkeit: Redis kann als Cluster betrieben werden, um hohe Lasten zu bewältigen und Ausfallsicherheit zu gewährleisten.

Muster 1: Cache-Aside (Lazy Loading)

Dies ist das gängigste und am einfachsten zu implementierende Caching-Muster. Es ist die Strategie, die ich im Beitrag “Turbo für Angular” bereits angerissen habe.

Der Workflow:

  1. Anfrage kommt an: Der Go-Service empfängt eine Anfrage für bestimmte Daten.
  2. Prüfe den Cache: Der Service versucht, die Daten aus Redis zu lesen.
  3. Cache Hit: Die Daten sind im Cache vorhanden. Sie werden sofort an den Client zurückgegeben. Die Datenbank wird nicht kontaktiert.
  4. Cache Miss: Die Daten sind nicht im Cache. Der Service liest die Daten aus der primären Datenbank (z.B. PostgreSQL).
  5. Fülle den Cache: Der Service schreibt die aus der Datenbank gelesenen Daten in Redis (mit einer TTL).
  6. Gib Daten zurück: Die Daten werden an den Client zurückgegeben.

Vorteile:

  • Resilienz: Wenn der Redis-Cache ausfällt, funktioniert die Anwendung immer noch (wenn auch langsamer), da sie auf die Datenbank zurückgreifen kann.
  • Nur benötigte Daten werden gecached: Der Cache wird nur mit Daten gefüllt, die auch tatsächlich angefragt werden.

Konzeptioneller Go-Code:

func GetUser(ctx context.Context, userID string, rdb *redis.Client, db *sql.DB) (*User, error) {
    // 1. Prüfe den Cache
    val, err := rdb.Get(ctx, "user:"+userID).Bytes()
    if err == nil {
        // Cache Hit
        var u User
        json.Unmarshal(val, &u)
        return &u, nil
    }

    // 2. Cache Miss: Lese aus DB
    var u User
    // ... Logik zum Lesen des Users aus der DB ...
    if err != nil {
        return nil, err
    }

    // 3. Fülle den Cache
    bytes, _ := json.Marshal(u)
    rdb.Set(ctx, "user:"+userID, bytes, 10*time.Minute)

    return &u, nil
}

Muster 2: Write-Through

Bei diesem Muster wird der Cache bei jedem Schreibvorgang synchron aktualisiert.

Der Workflow:

  1. Schreib-Anfrage kommt an: Der Go-Service erhält eine Anfrage, um Daten zu erstellen oder zu aktualisieren.
  2. Schreibe in die Datenbank: Der Service schreibt die neuen Daten in die primäre Datenbank.
  3. Schreibe in den Cache: Direkt danach schreibt der Service dieselben Daten auch in den Redis-Cache.
  4. Bestätige den Client: Erst wenn beide Schreibvorgänge erfolgreich waren, wird die Anfrage bestätigt.

Vorteile:

  • Hohe Datenkonsistenz: Der Cache ist (fast) immer aktuell. Leseanfragen erhalten selten veraltete Daten.

Nachteile:

  • Höhere Schreiblatenz: Jede Schreiboperation dauert länger, da auf zwei Systeme gewartet werden muss.

Wann ich es einsetze: Bei Daten, die häufiger gelesen als geschrieben werden, aber bei denen es sehr wichtig ist, dass nach einer Änderung sofort die aktuellen Daten gelesen werden.

Muster 3: Write-Back (Write-Behind)

Dies ist das performanteste, aber auch komplexeste Muster.

Der Workflow:

  1. Schreib-Anfrage kommt an: Der Go-Service schreibt die Daten nur in den schnellen Redis-Cache. Die Antwort an den Client erfolgt sofort.
  2. Asynchrones Schreiben: Ein separater Prozess oder eine Goroutine schreibt die Daten aus dem Cache verzögert und gebündelt in die langsame primäre Datenbank.

Vorteile:

  • Extrem niedrige Schreiblatenz: Die Anwendung fühlt sich für den Benutzer extrem schnell an.
  • Reduzierte Datenbanklast: Schreibvorgänge können gebündelt werden.

Nachteile:

  • Risiko von Datenverlust: Wenn der Redis-Cache ausfällt, bevor die Daten in die Datenbank geschrieben wurden, sind diese Daten verloren.
  • Komplexität: Die Implementierung ist deutlich komplexer.

Wann ich es einsetze: Für Anwendungsfälle, bei denen eine extrem hohe Schreibleistung erforderlich ist und der Verlust von Daten für einen kurzen Zeitraum akzeptabel ist (z.B. Zählen von “Likes”, Aufzeichnen von Metriken).

Fazit: Caching ist eine strategische Entscheidung

Es gibt nicht “das eine” richtige Caching-Muster. Die Wahl zwischen Cache-Aside, Write-Through und Write-Back hängt immer vom spezifischen Anwendungsfall, den Konsistenzanforderungen und der Fehlertoleranz ab. Die Kombination aus Go und Redis gibt mir jedoch die Werkzeuge an die Hand, um für jedes Szenario die passende, hochperformante Caching-Strategie zu implementieren. Ein gut durchdachter Caching-Layer ist oft der entscheidende Faktor, der eine gute API zu einer herausragenden API macht.


Ist die Performance Ihrer Microservices durch langsame Datenbankzugriffe limitiert? Ich helfe Ihnen bei der Konzeption und Implementierung einer maßgeschneiderten Caching-Strategie mit Go und Redis. Lassen Sie uns gemeinsam die Antwortzeiten Ihrer APIs drastisch reduzieren und Ihre Infrastruktur entlasten. Kontaktieren Sie mich für eine Performance-Architektur-Beratung.