Maison  >  Article  >  base de données  >  Comment résoudre le problème d'avalanche de cache Redis

Comment résoudre le problème d'avalanche de cache Redis

WBOY
WBOYavant
2023-06-03 09:46:021753parcourir

La couche cache transporte un grand nombre de requêtes, protégeant efficacement la couche de stockage. Cependant, si un grand nombre de requêtes arrivent à la couche de stockage en raison d'un grand nombre de pannes de cache ou si l'ensemble du cache ne peut pas fournir de services, la charge sur la couche de stockage augmentera (un grand nombre de requêtes interrogent la base de données). C'est le scénario de l'avalanche de cache ;

Pour résoudre l'avalanche de cache, vous pouvez partir des points suivants :

1 Gardez la couche de cache hautement disponible

Utilisez le mode sentinelle Redis ou la méthode de déploiement de cluster Redis, c'est-à-dire individuel. Les nœuds Redis sont hors ligne. L'intégralité de la couche de cache est toujours disponible. De plus, Redis peut être déployé dans plusieurs salles informatiques, de sorte que même en cas de panne de la salle informatique, la couche de cache reste hautement disponible.

2. Composants de limitation et de rétrogradation actuels

La couche de cache et la couche de stockage auront une probabilité d'erreurs et peuvent être considérées comme des ressources. En tant que système distribué avec une grande concurrence, si une ressource n'est pas disponible, cela peut provoquer des exceptions lorsque tous les threads obtiennent cette ressource, provoquant l'indisponibilité de l'ensemble du système. La rétrogradation est tout à fait normale dans les systèmes à haute concurrence. Par exemple, dans les services de recommandation, si le service de recommandation personnalisé n'est pas disponible, vous pouvez rétrograder pour compléter les données du point d'accès afin que l'intégralité du service de recommandation ne soit pas indisponible. Les composants courants de dégradation limitant le courant incluent Hystrix, Sentinel, etc.

3. Le cache n'expire pas

Les clés enregistrées dans Redis n'expireront jamais, il n'y aura donc pas de problème de défaillance d'un grand nombre de caches en même temps, mais Redis aura alors besoin de plus d'espace de stockage.

4. Optimiser le délai d'expiration du cache

Lors de la conception du cache, choisissez un délai d'expiration approprié pour chaque clé afin d'éviter qu'un grand nombre de clés ne s'invalident en même temps, provoquant une avalanche de cache.

5. Utilisez le verrouillage mutex pour reconstruire le cache

Dans les scénarios à haute concurrence, afin d'éviter qu'un grand nombre de requêtes n'atteignent la couche de stockage pour interroger les données et reconstruire le cache en même temps, vous pouvez utiliser le contrôle de verrouillage mutex, comme l'interrogation de données au niveau de la couche de cache en fonction de la clé. Lorsque la couche de cache est atteinte, la clé est verrouillée, puis les données sont interrogées depuis la couche de stockage, les données sont écrites dans la couche de cache et enfin le verrou est libéré. . Si d'autres threads constatent que l'acquisition du verrou échoue, laissez-le dormir pendant un certain temps et réessayez. Concernant le type de verrouillage, si vous êtes dans un environnement autonome, vous pouvez utiliser Lock sous le package simultané Java. Si vous êtes dans un environnement distribué, vous pouvez utiliser le verrouillage distribué (méthode SETNX dans Redis).

Pseudo-code de cache de reconstruction de verrouillage Mutex dans un environnement distribué

/**
 * 互斥锁建立缓存
 *
 **/
public String get(String key) {
   // redis中查询key对应的value
   String value = redis.get(key);
   // 缓存未命中
   if (value == null) {
      // 互斥锁
      String key_mutex_lock = "mutex:lock" + key; 
      // 互斥锁加锁成功
      if(redis.setnx(key_mutex_lock,"1")) { // 返回 0(false),1(true)
          try {
              // 设置互斥锁超时时间,这里设置的是锁的失效时间,而不是key的失效时间
              redis.expire(key_mutex_lock,3*60);
              // 从数据库查询
              value = db.get(key);
              // 数据写入缓存
              redis.set(key,value);
            
          } finally {
               // 释放锁
              boolean keyExist = jedis.exists(key_mutex_lock);
              if(keyExist){
                  redis.delete(key_mutex_lock);
               }
      } else { 
              // 加锁失败,线程休息50ms后重试
               Thread.sleep(50);
               return get(key); // 直接返回缓存结果  
     }
   }
}

Utilisation du verrouillage distribué Redis pour implémenter la reconstruction de cache dans un environnement distribué L'avantage est que l'idée de conception est simple et la cohérence des données est garantie ; augmente, et il y a peut faire attendre l'utilisateur. Supposons que dans des conditions de concurrence élevée, la clé est verrouillée lors de la reconstruction du cache. S'il y a actuellement 1 000 requêtes simultanées, 999 d'entre elles sont bloquées, ce qui entraîne le blocage et l'attente de 999 requêtes utilisateur.

6. Reconstruction asynchrone du cache

Dans ce schéma, une stratégie asynchrone est adoptée pour construire le cache. Les threads seront obtenus à partir du pool de threads pour construire le cache de manière asynchrone, afin que toutes les requêtes n'atteignent pas directement la couche de stockage. . Chaque Redis dans ce schéma Délai d'expiration logique de maintenance des clés Lorsque le délai d'expiration logique est inférieur à l'heure actuelle, cela signifie que le cache actuel a expiré et que le cache doit être mis à jour. la valeur dans le cache est renvoyée directement. Par exemple, dans Redis, le délai d'expiration de la clé est défini sur 60 minutes et le délai d'expiration logique dans la valeur correspondante est défini sur 30 minutes. De cette manière, lorsque la clé atteint le délai d'expiration logique de 30 minutes, le cache de cette clé peut être mis à jour de manière asynchrone, mais pendant la période de mise à jour du cache, l'ancien cache est toujours disponible. Cette méthode de reconstruction de cache asynchrone peut empêcher efficacement l'invalidation d'un grand nombre de clés en même temps.

/**
  *  异步重建缓存: ValueObject为对应的封装的实体模型
  *
  **/
public String get(String key) {
    // 重缓存中查询key对应的ValueObject对象
    ValueObject valueObject = redis.get(key);
    // 获取存储中对应的value值
    String value = valueObject.getValue();
    // 获取实体模型中的缓存过期的时间:timeOut = 设置缓存时的当前时间+过期时间(如30秒,60秒等等)
    long logicTimeOut = valueObject.getTimeOut();  // 等位换算为long类型
    // 当前可以在逻辑上失效
    if (logicTimeOut <= System.currentTimeMillis()) {
         // 异步更新缓存
         threadPool.execute(new Runnable() {
             String key_mutex_lock = "mutex_lock" + key;
              // 互斥锁加锁成功
      if(redis.setnx(key_mutex_lock,"1")) { // 返回 0(false),1(true)
          try {
              // 设置互斥锁超时时间,这里设置的是锁的失效时间,而不是key的失效时间
              redis.expire(key_mutex_lock,3*60);
              // 从数据库查询
              dbValue = db.get(key);
              // 数据写入缓存
              redis.set(key,dbValue);
            
          } finally {
              // 释放锁
              boolean keyExist = jedis.exists(key_mutex_lock);
              if(keyExist){
                  redis.delete(key_mutex_lock);
               }
             
         }
      } else { 
             
              
             }
             
         
         });
       return value; // 直接返回缓存结果  
    }
}

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer