Mise en cache en Go : comment accélérer votre application de 100 fois sans perdre la raison

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Pourquoi votre application Go ralentit-elle ? Vous avez probablement oublié le cache



Vous avez écrit l'application Go parfaite. Vous êtes fier des goroutines, des canaux et du compilateur rapide. Mais lorsque la charge atteint 1000 RPS, tout commence à grincer. La base de données plante, l'API répond en 5 secondes, les utilisateurs partent chez les concurrents.



Une situation familière ? Bienvenue dans le monde où, sans mise en cache en Go, vous n'irez nulle part. Dans cet article, nous allons voir comment transformer votre application tortue en guépard. Sans blabla, avec des exemples concrets et des solutions prêtes à l'emploi.



Au fait, je m'appelle Python, et voici PythonLib. Oui, oui, je sais que nous parlons de Go. Mais le bon code ne connaît pas les frontières des langages, et nous écrivons des articles utiles sur tout. C'est parti !



Qu'est-ce que la mise en cache et pourquoi en a-t-on besoin en Go ?



La mise en cache des données consiste à stocker les données fréquemment demandées dans un stockage rapide (mémoire vive, Redis, fichiers) afin de ne pas solliciter à chaque fois une source lente (base de données, API externe, système de fichiers).



Go est un langage pour les systèmes à forte charge. Mais même en Go, on peut écrire lentement si on n'utilise pas le cache. Imaginez : vous demandez 1000 fois par seconde la liste des pays depuis PostgreSQL. Au lieu de 1000 requêtes à la base de données, vous pouvez n'en faire qu'une seule, et les 999 autres fois, servir les données depuis le cache in-memory Go.



Résultat : le temps de réponse passe de 50 ms à 0,5 ms. Une différence de 100 fois. Ce n'est pas de la magie, c'est une architecture bien pensée.



Quand le cache est-il obligatoire ?

  • Forte charge (1000+ requêtes par seconde)
  • API externes lentes (paiements, météo, taux de change)
  • Calculs lourds (rapports, analyses)
  • Données de référence fréquemment demandées (listes de villes, catégories de produits)


Stratégies de mise en cache : choisir la bonne



Pour ne pas transformer le cache en anti-patron, il faut choisir la bonne stratégie de mise en cache. Voici les principales options qui fonctionnent en Go :



1. Cache-Aside (mise en cache paresseuse)

Le patron le plus populaire. L'application vérifie d'abord le cache. Si les données ne sont pas présentes (cache miss), elle va dans la base de données, enregistre le résultat dans le cache et le renvoie au client.



func GetUser(cache *redis.Client, db *sql.DB, userID int) (*User, error) {    // On essaie de récupérer depuis le cache    val, err := cache.Get(ctx, "user:"+strconv.Itoa(userID)).Result()    if err == nil {        user := &User{}        json.Unmarshal([]byte(val), user)        return user, nil    }

// Cache miss — on va dans la base de données user, err := db.Query("SELECT * FROM users WHERE id = ?", userID) if err != nil { return nil, err }

// On enregistre dans le cache pour 1 heure data, _ := json.Marshal(user) cache.Set(ctx, "user:"+strconv.Itoa(userID), data, time.Hour)

return user, nil}


2. Write-Through (écriture directe)

Les données sont écrites simultanément dans le cache et dans la base de données. Avantage : le cache est toujours à jour. Inconvénient : l'écriture est légèrement plus lente.



3. TTL (Time To Live)

La méthode la plus simple d'invalidation du cache. Les données vivent un temps fixe, puis sont automatiquement supprimées.



// Cache pour 5 minutescache.Set(ctx, "key", value, 5*time.Minute)


Implémentation d'un cache in-memory en Go fait maison



Parfois, on ne veut pas déployer Redis pour quelques clés. Pour ces cas, le cache in-memory Go est parfait. Écrivons une version simple mais fonctionnelle :



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), } // On lance une goroutine pour nettoyer les entrées expirées go c.cleanup() return c}

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

Blogs

Book Recommendations