Java Cluster Combat : mise à niveau d'une architecture unique vers une architecture en cluster (6) REDIS de cache distribué

Répertoire des articles de la série

Java Cluster Combat : mise à niveau d'une architecture unique vers une architecture de cluster (1) à l'aide de NGINX pour créer un cluster

Java Cluster Combat : Mise à niveau d'une architecture unique vers une architecture en cluster (2) Réaliser le partage de session

Java cluster combat : mise à niveau de l'architecture unique vers l'architecture de cluster (3) partage des fichiers téléchargés

Java Cluster Combat : mise à niveau d'une architecture unique vers une architecture de cluster (4) à l'aide de verrous distribués REDIS

Java Cluster Combat : Mise à niveau d'une architecture unique vers une architecture en cluster (5) Tâches planifiées



La mise en cache peut grandement améliorer les performances de lecture des données dans les scénarios à forte simultanéité. Le principe de fonctionnement du cache est de lire d'abord les données du cache, et s'il y a des données, elles seront renvoyées directement à l'utilisateur ; s'il n'y a pas de données, les données réelles seront lues à partir de la base de données et mises dans le cache , et enfin les données seront renvoyées à l'utilisateur.

Dans les scénarios à forte concurrence, l'utilisation du cache est un peu compliquée. Le code que nous écrivons ici est plus adapté aux scénarios à haute simultanéité. Si votre entreprise ne dispose pas de données massives à haute simultanéité, vous pouvez l'écrire plus simplement.

Nous utilisons une simple base de données de marchandises comme exemple pour discuter de l'utilisation de la mise en cache de REDIS.

adresse source

Le code de l'ensemble du projet est dans GITHUB :  https://github.com/Dengxd/JavaCluster     Tous les codes sources sont ici, GitHub n'est souvent pas connecté, il faut donc rafraichir plusieurs fois

Les principales fonctions métier sont dans la classe GoodsServiceImpl, nous nous concentrons donc sur cette classe

Période de validité du cache, réchauffement du cache et panne du cache

Regardez d'abord le code, c'est une méthode dans la classe GoodsServiceImpl :

public static Integer getExp(){
    Random r =new Random();
    return  REDIS_EXP+r.nextInt(3600);
}

Ce code génère un nombre aléatoire entre 36000 et 39600 comme période de validité du cache en secondes. Bien entendu, vous pouvez modifier la valeur spécifique en fonction de la situation de votre entreprise.

Dans la base de données, les données auxquelles on accède fréquemment sont appelées "données chaudes", et les données auxquelles on n'accède pas sont appelées "données froides". Les données impopulaires dans le cache seront automatiquement supprimées après la période de validité, et nous mettrons à jour sa période de validité lors de l'accès aux données chaudes, de sorte que la plupart des données conservées dans le cache sont des données chaudes, ce qui peut économiser de l'espace.

Pourquoi utiliser des nombres aléatoires au lieu de valeurs fixes ? Généralement, lorsque le système démarre, nous allons lire les données du point d'accès dans la base de données et les mettre dans le cache.C'est ce qu'on appelle le "préchauffage du cache". Si une valeur fixe est utilisée comme période de validité, ces données chaudes peuvent être automatiquement supprimées lorsque la période de validité expire. Ensuite, un grand nombre de demandes d'accès aux données chaudes doivent aller à la base de données pour récupérer les données, ce qui augmente la pression sur la base de données. C'est la "panne du cache". La perforation du cache signifie qu'il y a des données dans la base de données, mais qu'il n'y a pas de données dans le cache. Si des nombres aléatoires sont utilisés, il n'y aura pas un grand nombre de pannes de cache en même temps.

pénétration du cache

La pénétration du cache fait référence à l'interrogation de données qui n'existent pas. Étant donné que les données n'existent pas dans le cache ou dans la base de données, le cache et la base de données doivent être vérifiés une fois. Si un pirate envoie un grand nombre de requêtes de ce type, la base de données peut ne pas être en mesure de tenir le coup.

La solution consiste à ajouter un objet vide au cache lorsque la base de données ne trouve pas les données, comme indiqué ci-dessous :

goods = this.getById(id); //到数据库中查找这个id
if (goods != null) {//数据库找到数据
    //加入缓存
    bucket.set(JSON.toJSONString(goods), getExp(), TimeUnit.SECONDS);
} else { //数据库中找不到数据
    //对这个ID,在缓存中加入空对象“{}”
    bucket.set(NODATA, getNoDataExp(), TimeUnit.SECONDS);
}

Ce code se trouve dans la méthode get de la classe GoodsServiceImpl. Par exemple, si un utilisateur demande un produit avec un ID de 0, mais qu'il ne se trouve pas dans notre base de données, nous ajouterons l'objet vide "{}" avec un ID de 0 au cache.
La prochaine fois que l'utilisateur initie une demande de requête avec un ID de 0, nous trouvons l'ID dans le cache et constatons que les données dans le cache sont "{}", et nous renvoyons un nouvel objet, tous les champs sont NULL, donc nous n'avez pas besoin de vérifier à nouveau la base de données. Réduisez la charge sur la base de données. Ce code se trouve dans la méthode getFromRedis de la classe GoodsServiceImpl, comme suit :

if(NODATA.equals(strGood)){  //缓存中的数据是“{}”
    bucket.expire(getNoDataExp(),TimeUnit.SECONDS);//更新有效期
    return new Goods();//返回一个新对象,所有字段都是NULL
}

Hot Data Rebuild Cache et Double Check Lock

Considérons un scénario particulier. Un grand V a de nombreux fans. Il recommande un produit impopulaire (pas dans le cache) aux fans. Des milliers de fans recherchent immédiatement ce produit, mais le cache ne le trouve pas, alors ils arrivent tous Vérifiez dans la base de données (encore une fois la panne du cache), et après avoir trouvé les données, écrivez-les dans le cache. Est-ce trop de vérifier la base de données mille fois et d'écrire le cache mille fois pour mille utilisateurs en même temps ? Nous devons donc ajouter un verrou distribué et laisser uniquement le premier utilisateur vérifier la base de données et écrire dans le cache. Après avoir écrit le cache, tous les autres utilisateurs iront dans le cache pour récupérer des données.
Ce code se trouve dans la méthode get de GoodsServiceImpl :

goods=getFromRedis(bucket,key); 
if(goods!=null){  //缓存中有数据
    return goods;  //直接返回了
}
//缓存中没数据
//加一个分布式锁,只让第一个用户查数据库,写缓存
RLock createCacheLock=redissonClient.getLock(PREFIX_LOCK_CREATE_CACHE+id);
createCacheLock.lock();
try{
    //因为第一个用户已经把数据加到缓存中了,
    //所以第二个用户,第三个用户,……,第N个用户,
    //得到锁之后,再到缓存中找一次
    goods=getFromRedis(bucket,key);
    if(goods!=null){  //缓存中已经有数据了
        return goods; //直接返回
    }
//缓存中没找到数据,恭喜你,你是第一个用户,
//下面的代码,查数据库,写缓存
。。。。。。。。。。。。。。。
。。。。。。。。。。。。。。。

Il s'agit du fameux verrou à double détection, car le cache est vérifié une fois avant que le verrou ne soit verrouillé, et le cache est vérifié à nouveau après l'obtention du verrou, c'est ce qu'on appelle la double détection. Quant à la notoriété, c'est parce qu'elle est tellement incompréhensible qu'elle confond beaucoup de chaussures pour enfants. À l'ère pré-internet, je ne le voyais que dans le code du système d'exploitation. En le voyant dans le business Internet maintenant, j'ai l'impression d'avoir vu une fée descendue du monde.

Peu importe si vous ne le comprenez pas, copiez-le simplement. Ces codes sont équivalents à un modèle, qui peut être directement appliqué à nos propres projets.

incohérence en double écriture

"Double écriture" signifie que deux threads écrivent dans la base de données et dans le cache en même temps, et "incohérence" signifie que le contenu du cache et de la base de données est incohérent. Regardez l'image ci-dessous:

 

Les deux threads écrivent d'abord dans la base de données, puis mettent à jour le cache. Supposons que le thread 1 change le prix de la marchandise à 1 yuan et que le thread 2 change le prix de la marchandise à 2 yuan. Le thread 1 écrit d'abord dans la base de données, modifie le prix dans la base de données à 1 yuan, puis il reste bloqué. À ce moment, le thread 2 commence à écrire dans la base de données, changeant le prix dans la base de données à 2 yuans, puis le thread 2 met à jour le cache, changeant le prix dans le cache à 2 yuans. Enfin, le thread 1 n'est plus bloqué et continue de s'exécuter, changeant le prix dans le cache à 1 yuan.

Le problème se pose, le prix dans la base de données est de 2 yuans et le prix dans le cache est de 1 yuan, ce qui est l'incohérence de la double écriture.

Si votre entreprise peut tolérer cette incohérence, une fois le cache expiré et les données du cache effacées, il n'y aura plus d'incohérence. Si votre entreprise doit s'assurer que le cache de la base de données est cohérent, nous devons ajouter des verrous.

verrouillage en lecture-écriture

Le verrou ajouté cette fois est un verrou en lecture-écriture. L'avantage d'un verrou en lecture-écriture est que plusieurs verrous en lecture peuvent être exécutés ensemble, ce qui est plus adapté aux applications Internet qui lisent plus et écrivent moins. Les verrous en écriture et les verrous en lecture s'excluent mutuellement, et les verrous en écriture et les verrous en écriture s'excluent également mutuellement. C'est-à-dire que si un verrou en lecture obtient le verrou, d'autres verrous en lecture peuvent continuer à s'exécuter, mais le verrou en écriture doit attendre. Si un verrou en écriture est acquis, tous les autres verrous doivent attendre.

Nous ajoutons le verrou de lecture à l'endroit où la base de données est lue et le verrou d'écriture à l'endroit où la base de données est ajoutée, modifiée et supprimée.

Le code de verrouillage lu est le suivant :

//读取数据库,用读锁
RReadWriteLock updateLock=redissonClient.getReadWriteLock(PREFIX_LOCK_UPDATE+id);
RLock rLock=updateLock.readLock();
rLock.lock();

Le code de verrouillage en écriture est le suivant :

//修改数据库,用写锁
RReadWriteLock updateLock=redissonClient.getReadWriteLock(PREFIX_LOCK_UPDATE+goods.getId());
RLock wLock=updateLock.writeLock();
wLock.lock();

cache avalanche

L'avalanche de cache signifie qu'après que la couche de cache ne peut pas prendre en charge ou tombe en panne, un grand nombre de requêtes atteindront la couche de stockage, et le nombre d'appels à la couche de stockage augmentera fortement, provoquant une cascade de la couche de stockage, de sorte que les services qui dépendent sur le cache et la couche de stockage Des problèmes peuvent également survenir, comme une avalanche, provoquant le plantage de tout le système. Les solutions courantes sont la limitation ou l'utilisation de clusters de cache.


Je suppose que tu aimes

Origine blog.csdn.net/dengxiaodai/article/details/129901783
conseillé
Classement