Caching in Go: So beschleunigen Sie Ihre Anwendung um das 100-fache, ohne verrückt zu werden

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Warum Ihre Go-Anwendung lahmt? Wahrscheinlich haben Sie den Cache vergessen



Sie haben eine perfekte Anwendung in Go geschrieben. Sie sind stolz auf Goroutinen, Kanäle und den schnellen Compiler. Aber wenn die Last 1000 RPS erreicht, beginnt alles zu knirschen. Die Datenbank fällt aus, die API antwortet in 5 Sekunden, die Nutzer gehen zur Konkurrenz.



Kommt Ihnen das bekannt vor? Willkommen in der Welt, in der Sie ohne Caching in Go nicht auskommen. In diesem Artikel schauen wir uns an, wie Sie Ihre lahme Anwendung in einen Geparden verwandeln. Ohne Umschweife, mit echten Beispielen und fertigen Lösungen.



Übrigens, ich heiße Python, und das ist PythonLib. Ja, ja, ich weiß, dass wir über Go sprechen. Aber guter Code kennt keine Sprachgrenzen, und wir schreiben nützliche Artikel über alles. Los geht's!



Was ist Caching und warum braucht man es in Go?



Daten-Caching bedeutet, häufig angefragte Daten in einem schnellen Speicher (RAM, Redis, Dateien) zu speichern, um nicht jedes Mal die langsame Quelle (Datenbank, externe API, Dateisystem) anzufragen.



Go ist eine Sprache für hochlastige Systeme. Aber selbst in Go kann man langsame Programme schreiben, wenn man keinen Cache verwendet. Stellen Sie sich vor: Sie fragen 1000 Mal pro Sekunde eine Liste von Ländern aus PostgreSQL ab. Statt 1000 Datenbankabfragen könnten Sie nur eine machen und die restlichen 999 Mal die Daten aus dem In-Memory-Cache Go ausliefern.



Ergebnis: Die Antwortzeit sinkt von 50 ms auf 0,5 ms. Ein Unterschied um das 100-fache. Das ist keine Magie, sondern durchdachte Architektur.



Wann ist ein Cache zwingend erforderlich?

  • Hohe Last (1000+ Anfragen pro Sekunde)
  • Langsame externe APIs (Zahlungsdienste, Wetter, Wechselkurse)
  • Aufwändige Berechnungen (Berichte, Analysen)
  • Häufig angefragte Referenzdaten (Städtelisten, Produktkategorien)


Caching-Strategien: Die richtige auswählen



Um den Cache nicht zum Anti-Pattern zu machen, müssen Sie die richtige Caching-Strategie wählen. Hier sind die wichtigsten Optionen, die in Go funktionieren:



1. Cache-Aside (Lazy Caching)

Das beliebteste Muster. Die Anwendung prüft zuerst den Cache. Wenn die Daten nicht vorhanden sind (Cache Miss), geht sie zur Datenbank, speichert das Ergebnis im Cache und gibt es an den Client zurück.



func GetUser(cache *redis.Client, db *sql.DB, userID int) (*User, error) {    // Versuchen, aus dem Cache zu holen    val, err := cache.Get(ctx, "user:"+strconv.Itoa(userID)).Result()    if err == nil {        user := &User{}        json.Unmarshal([]byte(val), user)        return user, nil    }

// Cache-Fehler – zur Datenbank gehen user, err := db.Query("SELECT * FROM users WHERE id = ?", userID) if err != nil { return nil, err }

// Für 1 Stunde im Cache speichern data, _ := json.Marshal(user) cache.Set(ctx, "user:"+strconv.Itoa(userID), data, time.Hour)

return user, nil}


2. Write-Through (Durchgehendes Schreiben)

Daten werden gleichzeitig in den Cache und in die Datenbank geschrieben. Vorteil: Der Cache ist immer aktuell. Nachteil: Das Schreiben ist etwas langsamer.



3. TTL (Time To Live)

Der einfachste Weg der Cache-Invalidierung. Die Daten leben für eine festgelegte Zeit und werden dann automatisch gelöscht.



// Cache für 5 Minutencache.Set(ctx, "key", value, 5*time.Minute)


Implementierung eines In-Memory-Cache in Go von Grund auf



Manchmal möchte man nicht extra Redis für ein paar Schlüssel aufsetzen. Für solche Fälle eignet sich ein In-Memory-Cache Go hervorragend. Schreiben wir eine einfache, aber funktionierende Version:



package cache

import ( "sync" "time")

type Item struct { Value interface{} Expiration int64}

type Cache struct { items map[string]Item mu sync.RWMutex}

func New() *Cache { c := &Cache{ items: make(map[string]Item), } // Goroutine zum Bereinigen abgelaufener Einträge starten go c.cleanup() return c}

func (c *Cache) Set(key string, value interface{}, ttl time.Duration) { c.mu.Lock() defer c.mu.Un

Blogs

Buchempfehlungen