0Pricing
AWS Solutions Architect · Leçon

Stratégies de mise en cache : chargement différé et écriture directe

Mettez en œuvre le chargement différé pour alimenter le cache en cas d’absence, ainsi que l’écriture directe pour maintenir sa cohérence avec chaque écriture dans la base de données.

Stratégies de mise en cache : chargement différé et écriture directe est une leçon AWS Solutions Architect gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage AWS Solutions Architect, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AWS Solutions Architect comprend 4 leçons au total.

Pourquoi les stratégies de mise en cache sont importantes

Insérer un cache entre votre application et votre base de données nécessite une stratégie de mise en cache : un ensemble de règles qui déterminent quand les données sont placées dans le cache, quand elles y sont lues et quand elles en sont supprimées. Une stratégie mal choisie entraîne soit des données obsolètes (le cache renvoie des valeurs dépassées), soit des échecs sur cache froid (le cache est vide et chaque requête atteint la base de données), soit une surcharge due à un afflux de requêtes (de nombreuses requêtes simultanées pour la même clé absente atteignent toutes la base de données en même temps). Les deux stratégies fondamentales sont le chargement différé et l’écriture directe.

Chargement différé (Cache-Aside) : fonctionnement

Le chargement différé (également appelé cache-aside) est le modèle de mise en cache le plus courant. L’application vérifie d’abord le cache. En cas de succès dans le cache, les données sont renvoyées directement depuis celui-ci, par le chemin le plus rapide. En cas d’échec dans le cache, l’application récupère les données depuis la base de données, écrit le résultat dans le cache avec un TTL, puis le renvoie à l’appelant. Le cache ne contient que les données réellement demandées, d’où le terme « différé ». La requête suivante pour la même clé trouvera les données dans le cache.

# Lazy loading pattern (Python with redis-py)
def get_user(user_id, redis_client, db):
    cache_key = f'user:{user_id}'

    # 1. Check cache
    cached = redis_client.get(cache_key)
    if cached:
        return json.loads(cached)   # Cache HIT

    # 2. Cache MISS: fetch from DB
    user = db.query('SELECT * FROM users WHERE id = %s', user_id)

    # 3. Populate cache with TTL of 300 seconds
    redis_client.setex(cache_key, 300, json.dumps(user))

    return user

Chargement différé : avantages

Le chargement différé présente trois avantages principaux : seules les données demandées sont mises en cache : le cache n’est pas rempli de données que personne ne lit, ce qui permet d’utiliser efficacement la mémoire. Un cache froid n’empêche pas l’application de fonctionner : en cas d’échec dans le cache, l’application se rabat sur la base de données ; ainsi, même si ElastiCache est redémarré ou si un nœud tombe en panne, l’application continue de fonctionner, avec une latence plus élevée. Le cache reste toujours éventuellement cohérent avec la base de données, car les données obsolètes expirent via TTL, même si certaines mises à jour n’ont pas été prises en compte.

Chargement différé : inconvénients

Le chargement différé présente trois inconvénients principaux : les échecs dans le cache sont coûteux : trois opérations (vérification du cache, lecture de la base de données et écriture dans le cache) au lieu d’une seule en cas de succès, ce qui augmente la latence des requêtes à froid. Données obsolètes : après une mise à jour de la base de données, le cache continue de fournir l’ancienne valeur jusqu’à l’expiration du TTL ou à l’invalidation explicite de la clé, créant une fenêtre d’incohérence. Surcharge due à un afflux de requêtes : lorsqu’une clé populaire expire, de nombreuses requêtes simultanées subissent un échec dans le cache et sollicitent la base de données en parallèle, ce qui peut la surcharger.

# Mitigating cache stampede with a probabilistic early expiration
# (Refresh the key before it expires to avoid simultaneous misses)
def get_with_stampede_protection(key, redis_client, db_fetch_fn, ttl=300):
    value = redis_client.get(key)
    ttl_remaining = redis_client.ttl(key)

    # Probabilistically refresh before expiry
    if value is None or (ttl_remaining < 30 and random.random() < 0.1):
        value = db_fetch_fn()
        redis_client.setex(key, ttl, json.dumps(value))
    return json.loads(value)

Écriture directe : fonctionnement

Avec la stratégie d’écriture directe, chaque écriture dans la base de données est également effectuée dans le cache au même moment. L’application écrit à la fois dans le cache et dans la base de données dans le cadre de la même opération, ou la base de données déclenche une mise à jour du cache. Le cache est donc toujours synchronisé avec la base de données : il n’existe aucune fenêtre de données obsolètes. L’écriture directe garantit que les données du cache sont toujours à jour ; les lectures suivantes de données récemment écrites réussissent donc toujours dans le cache.

# Write-through pattern (Python)
def update_user(user_id, user_data, redis_client, db):
    # 1. Write to database FIRST
    db.execute('UPDATE users SET name=%s WHERE id=%s',
                (user_data['name'], user_id))

    # 2. Update cache immediately (write-through)
    cache_key = f'user:{user_id}'
    redis_client.setex(cache_key, 3600, json.dumps(user_data))

    return user_data

# Every read is now a cache hit for recently updated data

Écriture directe : avantages

Avantages de l’écriture directe : les données du cache sont toujours à jour : il n’y a pas de données obsolètes, car le cache est mis à jour à chaque écriture. Les lectures sont toujours rapides : les données populaires qui sont fréquemment écrites sont toujours présentes dans le cache. Aucune surcharge due à un afflux de requêtes lors des lectures : les données étant préchargées avant leur lecture, les données récemment écrites ne provoquent aucun échec sur cache froid. Cette stratégie convient parfaitement aux charges de travail dominées par les lectures et comportant des mises à jour fréquentes, lorsque l’actualité des données est essentielle, par exemple pour les catalogues de produits, les systèmes de tarification ou les caches de profils utilisateur.

Écriture directe : inconvénients

Inconvénients de l’écriture directe : pénalité à l’écriture : chaque écriture entraîne deux opérations (base de données + cache), ce qui augmente la latence des écritures. Pollution du cache : les données sont mises en cache même si elles ne sont plus jamais lues (écrites une fois, jamais demandées), ce qui gaspille de la mémoire. Le redémarrage du cache crée un cache froid : si le cluster de cache est redémarré, toutes les données écrites de manière proactive sont perdues et le cache doit être repeuplé par des écritures ou par un processus de préchauffage. Associez l’écriture directe à un TTL pour éviter la croissance illimitée de données rarement lues dans le cache.

Combiner le chargement différé et l’écriture directe

En pratique, de nombreux systèmes de production combinent les deux stratégies : utilisez l’écriture directe pour les données fréquemment mises à jour et fréquemment lues (comme les sessions utilisateur ou les prix actuels), et le chargement différé pour les données rarement mises à jour mais fréquemment lues (comme les descriptions de produits ou le contenu d’articles). Définissez des TTL appropriés pour les deux stratégies : les clés gérées par écriture directe reçoivent un TTL long, puisque les données sont toujours à jour ; les clés chargées de manière différée reçoivent un TTL plus court afin de limiter la fenêtre de données obsolètes. Cette approche hybride maximise le taux de succès du cache tout en réduisant au minimum l’obsolescence.

# Hybrid: write-through for sessions, lazy loading for product data

# Write-through for session data (always fresh, critical)
def save_session(session_id, data, redis_client, db):
    db.upsert('sessions', session_id, data)
    redis_client.setex(f'session:{session_id}', 3600, json.dumps(data))

# Lazy loading for product catalog (infrequent updates OK)
def get_product(product_id, redis_client, db):
    cached = redis_client.get(f'product:{product_id}')
    if cached:
        return json.loads(cached)
    product = db.query('SELECT * FROM products WHERE id=%s', product_id)
    redis_client.setex(f'product:{product_id}', 86400, json.dumps(product))
    return product

Principes de conception du TTL

Le Time-To-Live (TTL) des données mises en cache contrôle la durée maximale pendant laquelle elles peuvent être obsolètes ainsi que l’empreinte mémoire du cache. Concevez les TTL en fonction de la fréquence de mise à jour des données (les données de session changent souvent → TTL court ; le contenu statique change rarement → TTL long), de la tolérance à l’obsolescence (prix financiers → très court ; texte d’un article de blog → quelques heures ou quelques jours) et de la capacité mémoire du cache (mémoire limitée → TTL plus court pour supprimer plus rapidement les données obsolètes). Définissez toujours un TTL : ne mettez jamais en cache des données sans expiration, sinon le cache finira par se remplir de données obsolètes.

# TTL examples by data type

# API rate limit counter: 60 seconds
redis_client.setex(f'ratelimit:{ip}', 60, count)

# User session: 30 minutes
redis_client.setex(f'session:{id}', 1800, json.dumps(session))

# Product catalog: 24 hours
redis_client.setex(f'product:{id}', 86400, json.dumps(product))

# Stock price: 10 seconds
redis_client.setex(f'price:{symbol}', 10, price)

# Static site content: 7 days
redis_client.setex(f'page:{slug}', 604800, html_content)

Invalidation du cache lors d’une mise à jour

Au lieu de compter uniquement sur l’expiration du TTL, vous pouvez invalider explicitement (supprimer) les clés du cache lorsque les données sous-jacentes changent. Cela élimine entièrement la fenêtre de données obsolètes. Modèles courants : suppression lors de l’écriture (supprimer la clé du cache après chaque mise à jour de la base de données ; la lecture suivante repeuple le cache via le chargement différé) et invalidation pilotée par les événements (DynamoDB Streams ou la capture des modifications de RDS déclenche une fonction Lambda qui supprime les clés concernées). L’invalidation du cache est l’un des problèmes les plus difficiles des systèmes distribués ; plus la logique d’invalidation est simple, plus votre cache est fiable.

# Delete-on-write invalidation pattern
def update_product(product_id, new_data, redis_client, db):
    # Update the database
    db.execute('UPDATE products SET ... WHERE id=%s', (product_id,))

    # Invalidate the cache key
    redis_client.delete(f'product:{product_id}')

    # Also invalidate any list/search caches that may include this product
    redis_client.delete('products:list:page:1')
    redis_client.delete(f'products:category:{new_data["category_id"]}')

    # Next read will trigger lazy loading with fresh data

Mise en cache avec écriture en différé (Write-Back)

Un modèle moins courant, mais puissant, est l’écriture en différé (write-back) : l’application écrit uniquement dans le cache, puis celui-ci transfère de manière asynchrone les données vers la base de données en arrière-plan. Cette approche permet des écritures extrêmement rapides (uniquement en mémoire), au prix d’une perte potentielle de données si le cache tombe en panne avant le transfert. L’écriture en différé convient aux écritures très fréquentes et volumineuses lorsque les données peuvent être reconstruites ou que de petites pertes sont acceptables, par exemple pour les compteurs de succès, les événements analytiques ou les mises à jour de scores de jeux. ElastiCache ne prend pas nativement en charge l’écriture en différé ; celle-ci doit être implémentée au niveau de l’application.

# Write-behind pattern: write to cache, flush to DB asynchronously
# Application writes:
# redis_client.incr('post:42:views')      # fast, in-memory only

# Background job (runs every 60 seconds):
def flush_view_counts(redis_client, db):
    for key in redis_client.scan_iter('post:*:views'):
        count = redis_client.getdel(key)  # atomic get-and-delete
        post_id = key.split(':')[1]
        db.execute('UPDATE posts SET views = views + %s WHERE id = %s',
                   (int(count), post_id))

Vérification rapide

Vérifiez votre compréhension des concepts AWS Solutions Architect (SAA-C03) présentés dans cette leçon.

Récapitulatif de la leçon

Dans cette leçon, vous avez appris que le chargement différé peuple le cache lors des échecs de lecture et utilise efficacement la mémoire, mais peut fournir des données obsolètes jusqu’à l’expiration du TTL ; que l’écriture directe met à jour le cache à chaque écriture, éliminant les données obsolètes, mais gaspille de la mémoire pour les données jamais lues ; et que l’invalidation explicite supprime les clés du cache lors des mises à jour de la base de données afin d’éliminer les fenêtres de données obsolètes. Nous allons maintenant étudier le stockage des sessions et les modèles de classement avec ElastiCache.

Questions Fréquemment Posées

La leçon « Stratégies de mise en cache : chargement différé et écriture directe » est-elle gratuite ?

Oui — le texte complet de « Stratégies de mise en cache : chargement différé et écriture directe » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours AWS Solutions Architect, passe à CoddyKit PRO. Le cours AWS Solutions Architect comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Stratégies de mise en cache : chargement différé et écriture directe » ?

Mettez en œuvre le chargement différé pour alimenter le cache en cas d’absence, ainsi que l’écriture directe pour maintenir sa cohérence avec chaque écriture dans la base de données. Tu pratiques AWS Solutions Architect avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer AWS Solutions Architect ?

Aucune expérience préalable n'est requise. AWS Solutions Architect sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.

Combien de temps prend la leçon « Stratégies de mise en cache : chargement différé et écriture directe » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon AWS Solutions Architect ?

Oui. Chaque leçon AWS Solutions Architect inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Redis ou Memcached : choisir le moteur adapté
  2. Groupes de réplication Redis ElastiCache et mode cluster
  3. Stratégies de mise en cache : chargement différé et écriture directe
  4. Stockage des sessions et modèles de classements
← Retour à AWS Solutions Architect