


Comment implémenter le verrouillage distribué Redis et quels sont ses scénarios d'application
Introduction
Le verrouillage est un outil très courant dans le processus de développement. Vous devez le connaître, verrouillage pessimiste, verrouillage optimiste, verrouillage exclusif, verrouillage équitable, verrouillage injuste, etc., de nombreux concepts, si vous l'êtes. familier avec Java Si vous ne comprenez pas encore les verrous, vous pouvez vous référer à cet article : Les "verrous" Java qu'il faut mentionner Cet article est très complet Cependant, pour les débutants, connaître les concepts de ces verrous n'est peut-être pas possible. en raison du manque d'expérience pratique.Si vous ne comprenez pas les scénarios d'utilisation réels des verrous, vous pouvez utiliser les trois mots-clés Volatile, Synchronized et ReentrantLock pour assurer la sécurité des threads en Java. Cette partie des connaissances sera certainement demandée dans le. première série d'entretiens de base (vous devez les maîtriser).
Dans un système distribué, les technologies de verrouillage Java ne peuvent pas verrouiller le code sur deux machines en même temps, elles doivent donc être mises en œuvre via des verrous distribués. L'utilisation compétente des verrous distribués est également une compétence qui doit être maîtrisée par les principaux développeurs.
1. Intervieweur :
Avez-vous déjà rencontré un scénario dans lequel vous devez utiliser des verrous distribués ?
Analyse des problèmes : cette question est principalement utilisée à titre d'introduction. Vous devez d'abord comprendre dans quels scénarios les verrous distribués doivent être utilisés et quels problèmes les verrous distribués doivent résoudre. Dans ce principe, cela vous aidera à mieux comprendre le principe de mise en œuvre de. serrures distribuées.
Les scénarios d'utilisation des verrous distribués doivent généralement répondre aux scénarios suivants :
Le système est un système distribué et les verrous Java ne peuvent plus être verrouillés.
Exploitez des ressources partagées, telles que les seules données utilisateur de la bibliothèque.
L'accès synchrone, c'est-à-dire que plusieurs processus exploitent des ressources partagées en même temps.
Réponse : Laissez-moi vous donner un exemple de scénario dans lequel j'utilise des verrous distribués dans un projet :
Les points de consommation sont disponibles dans de nombreux systèmes, notamment les cartes de crédit, les sites de commerce électronique, l'échange de points contre des cadeaux, etc. Voici le fonctionnement des "points de consommation". Il s'agit d'un scénario typique où des verrous sont nécessaires.
Événement A :
En prenant comme exemple l'échange de points contre des cadeaux, le processus complet de consommation de points est simplement divisé en 3 étapes :
A1 : L'utilisateur sélectionne le produit, lance l'échange et soumet la commande.
A2 : Le système lit les points restants de l'utilisateur : détermine si les points actuels de l'utilisateur sont suffisants.
A3 : Les points utilisateurs seront déduits.
Événement B :
Le système distribue les points aux utilisateurs en trois étapes simples :
B1 : Calculez les points que l'utilisateur mérite pour la journée
B2 : Lisez les points originaux de l'utilisateur
B3 : Ajoutez cette application à l'original points Gagnez des points
Ensuite la question se pose, que se passe-t-il si l'utilisateur consomme des points et qu'il accumule des points en même temps (les points utilisateur sont exploités en même temps) ?
Hypothèse : pendant que l'utilisateur consomme des points, une tâche hors ligne consiste à calculer des points et à attribuer des points à l'utilisateur (par exemple, en fonction du montant de la consommation de l'utilisateur ce jour-là). suivre la logique est un peu alambiqué, alors soyez patient et comprenez-la.
L'utilisateur U dispose de 1 000 points (les points utilisateur d'enregistrement des données peuvent être compris comme des ressources partagées), et cet échange consommera 999 points.
Situation déverrouillée : Lorsque le programme de l'événement A atteint l'étape 2 de lecture des points, le résultat lu lors de l'opération A:2 est de 1000 points. Il est jugé que les points restants sont suffisants pour cet échange, puis l'étape 3 A est exécutée : Des points seront déduits pour 3 opérations (1000 - 999 = 1). Le résultat normal devrait toujours être de 1 point pour l'utilisateur. Mais l'événement B est également en cours d'exécution à ce moment-là. Cette fois, 100 points seront attribués à l'utilisateur U. Deux threads le feront en même temps (accès synchrone, il y aura la possibilité suivante : A:2 -). > B :2 -> A:3 -> B:3 , avant que A:3 ne soit terminé (points déduits, 1000 - 999), le total des points de l'utilisateur U est lu par le fil de l'événement B, et enfin celui de l'utilisateur U. total de points C'est devenu 1100 points, et j'ai échangé en vain un cadeau de 999 points, ce qui n'a évidemment pas répondu aux résultats escomptés.
Certaines personnes disent, comment est-il possible de faire fonctionner des points utilisateur en même temps par hasard, et le processeur est si rapide ? Tant qu'il y a suffisamment d'utilisateurs et que la concurrence est suffisamment grande, la loi de Murphy prendra effet tôt ou tard. Ce n'est qu'une question de temps avant que le bug ci-dessus n'apparaisse, et il peut être bloqué par l'industrie noire. À l'heure actuelle, en tant que développeur, si vous voulez résoudre ce danger caché, vous devez comprendre l'utilisation de. déverrouillage.
(Écrire du code est une chose rigoureuse !)
Java lui-même fournit deux implémentations de verrous intégrées, l'une est synchronisée implémentée par la JVM et le verrou fourni par le JDK, et de nombreuses classes d'opérations atomiques sont des threads sécurisés, lorsque votre application est une application autonome ou à processus unique, vous pouvez utiliser ces deux verrous pour implémenter des verrous.
Mais la plupart des systèmes actuels des sociétés Internet sont distribués. À l'heure actuelle, le verrou synchronisé fourni avec Java ne peut plus répondre aux exigences de verrouillage dans un environnement distribué, car le code sera déployé sur plusieurs machines dans l'ordre. Pour résoudre ce problème, les verrous distribués ont vu le jour. Les caractéristiques des verrous distribués sont multi-processus et la mémoire ne peut pas être partagée sur plusieurs machines physiques. La solution courante est basée sur l'interférence de la couche mémoire. basé sur Redis ou ZooKeeper Distributed Lock.
(Je ne peux pas l'analyser plus en détail, l'intervieweur n'est plus satisfait ?)
2. Intervieweur :
Méthode d'implémentation du verrouillage distribué Redis
Il existe actuellement deux méthodes principales d'implémentation pour résoudre le problème du verrouillage distribué : 1. L'un est basé sur le mode Redis Cluster et l'autre est... 2. Basé sur le mode cluster Zookeeper.
Donnez la priorité à la maîtrise de ces deux-là, et vous n'aurez pratiquement aucun problème à gérer l'entretien.
Réponse :
1. Verrouillage distribué basé sur Redis
Méthode 1 : Utilisez la commande setnx pour verrouiller
public static void wrongGetLock1(Jedis jedis, String lockKey, String requestId, int expireTime) { // 第一步:加锁 Long result = jedis.setnx(lockKey, requestId); if (result == 1) { // 第二步:设置过期时间 jedis.expire(lockKey, expireTime); } }
Explication du code :
La commande setnx
signifie définie si elle n'existe pas. Si la clé de verrouillage n'existe pas, stockez la clé dans Redis. Si le résultat renvoie 1 après un enregistrement réussi, cela signifie que le paramètre est réussi. S'il n'est pas 1, cela signifie un échec. . D'autres discussions l'ont déjà défini. setnx
命令,意思就是 set if not exist,如果lockKey不存在,把key存入Redis,保存成功后如果result返回1,表示设置成功,如果非1,表示失败,别的线程已经设置过了。
expire()
expire()
, définissez le délai d'expiration pour éviter un blocage. Supposons que si un verrou n'est pas supprimé après avoir été défini, alors le verrou équivaut à toujours exister, ce qui entraîne impasse. (À ce stade, je voudrais souligner un "mais" à l'intervieweur) Réfléchissez-y, quels sont les défauts de ma méthode ci-dessus ? Continuez à expliquer à l'intervieweur...Il y a deux étapes dans le verrouillage. La première étape est jedis.setnx, et la deuxième étape est jedis.expire pour définir le délai d'expiration de setnx et expirer. Il ne s'agit pas d'une opération atomique. Si le programme s'exécute Après avoir terminé la première étape, une exception s'est produite. La deuxième étape, jedis.expire(lockKey, expireTime) n'a pas été exécutée, ce qui signifie que le verrou n'a pas de délai d'expiration et qu'un blocage peut se produire. Comment améliorer ce problème ? Amélioration : public class RedisLockDemo { private static final String SET_IF_NOT_EXIST = "NX"; private static final String SET_WITH_EXPIRE_TIME = "PX"; /** * 获取分布式锁 * @param jedis Redis客户端 * @param lockKey 锁 * @param requestId 请求标识 * @param expireTime 超期时间 * @return 是否获取成功 */ public static boolean getLock(Jedis jedis, String lockKey, String requestId, int expireTime) { // 两步合二为一,一行代码加锁并设置 + 过期时间。 if (1 == jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime)) { return true;//加锁成功 } return false;//加锁失败 } }
public static void unLock(Jedis jedis, String lockKey, String requestId) { // 第一步: 使用 requestId 判断加锁与解锁是不是同一个客户端 if (requestId.equals(jedis.get(lockKey))) { // 第二步: 若在此时,这把锁突然不是这个客户端的,则会误解锁 jedis.del(lockKey); } }
public class RedisTool { private static final Long RELEASE_SUCCESS = 1L; /** * 释放分布式锁 * @param jedis Redis客户端 * @param lockKey 锁 * @param requestId 请求标识 * @return 是否释放成功 */ public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId)); if (RELEASE_SUCCESS.equals(result)) { return true; } return false; } }
- Client La fin appelle la méthode create() pour créer un nœud de séquence temporaire nommé "/dlm-locks/lockname/lock-".
- Le client appelle la méthode getChildren("lockname") pour obtenir tous les nœuds enfants créés.
- Une fois que le client a obtenu les chemins de tous les nœuds enfants, s'il constate que le nœud qu'il a créé à l'étape 1 a le plus petit numéro de séquence parmi tous les nœuds, il recherchera à la séquence qu'il a créée. Le numéro est-il classé en premier ? S'il est premier, alors on considère que ce client a obtenu le verrou, et qu'aucun autre client n'a obtenu le verrou avant lui.
- Si le nœud créé n'est pas le plus petit de tous les nœuds, alors il est nécessaire de surveiller le plus grand nœud avec un numéro de séquence plus petit que le nœud que vous avez créé, puis entrer dans l’état d’attente. Une fois le nœud enfant surveillé modifié, obtenez le nœud enfant et déterminez s'il faut obtenir le verrou.
方法一:
利用 Mysql 的锁表,创建一张表,设置一个 UNIQUE KEY 这个 KEY 就是要锁的 KEY,所以同一个 KEY 在mysql表里只能插入一次了,这样对锁的竞争就交给了数据库,处理同一个 KEY 数据库保证了只有一个节点能插入成功,其他节点都会插入失败。
这样 lock 和 unlock 的思路就很简单了,伪代码:
def lock : exec sql: insert into locked—table (xxx) values (xxx) if result == true : return true else : return false def unlock : exec sql: delete from lockedOrder where order_id='order_id'
方法二:
使用流水号+时间戳做幂等操作,可以看作是一个不会释放的锁。
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!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

Video Face Swap
Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Sujets chauds

Le mode Redis Cluster déploie les instances Redis sur plusieurs serveurs grâce à la rupture, à l'amélioration de l'évolutivité et de la disponibilité. Les étapes de construction sont les suivantes: Créez des instances de redis étranges avec différents ports; Créer 3 instances Sentinel, Moniteur Redis Instances et basculement; Configurer les fichiers de configuration Sentinel, ajouter des informations d'instance Redis de surveillance et des paramètres de basculement; Configurer les fichiers de configuration d'instance Redis, activer le mode de cluster et spécifier le chemin du fichier d'informations de cluster; Créer un fichier nœuds.conf, contenant des informations de chaque instance redis; Démarrez le cluster, exécutez la commande CREATE pour créer un cluster et spécifiez le nombre de répliques; Connectez-vous au cluster pour exécuter la commande d'informations de cluster pour vérifier l'état du cluster; faire

Comment effacer les données Redis: utilisez la commande flushall pour effacer toutes les valeurs de clé. Utilisez la commande flushdb pour effacer la valeur clé de la base de données actuellement sélectionnée. Utilisez SELECT pour commuter les bases de données, puis utilisez FlushDB pour effacer plusieurs bases de données. Utilisez la commande del pour supprimer une clé spécifique. Utilisez l'outil Redis-CLI pour effacer les données.

Pour lire une file d'attente à partir de Redis, vous devez obtenir le nom de la file d'attente, lire les éléments à l'aide de la commande LPOP et traiter la file d'attente vide. Les étapes spécifiques sont les suivantes: Obtenez le nom de la file d'attente: Nommez-le avec le préfixe de "Fitre:" tel que "Fitre: My-Quyue". Utilisez la commande LPOP: éjectez l'élément de la tête de la file d'attente et renvoyez sa valeur, telle que la file d'attente LPOP: My-Queue. Traitement des files d'attente vides: si la file d'attente est vide, LPOP renvoie NIL et vous pouvez vérifier si la file d'attente existe avant de lire l'élément.

L'utilisation de la directive Redis nécessite les étapes suivantes: Ouvrez le client Redis. Entrez la commande (Verbe Key Value). Fournit les paramètres requis (varie de l'instruction à l'instruction). Appuyez sur Entrée pour exécuter la commande. Redis renvoie une réponse indiquant le résultat de l'opération (généralement OK ou -err).

L'utilisation des opérations Redis pour verrouiller nécessite l'obtention du verrouillage via la commande setnx, puis en utilisant la commande Expire pour définir le temps d'expiration. Les étapes spécifiques sont les suivantes: (1) Utilisez la commande setnx pour essayer de définir une paire de valeurs de clé; (2) Utilisez la commande Expire pour définir le temps d'expiration du verrou; (3) Utilisez la commande del pour supprimer le verrouillage lorsque le verrouillage n'est plus nécessaire.

Sur CentOS Systems, vous pouvez limiter le temps d'exécution des scripts LUA en modifiant les fichiers de configuration Redis ou en utilisant des commandes Redis pour empêcher les scripts malveillants de consommer trop de ressources. Méthode 1: Modifiez le fichier de configuration Redis et localisez le fichier de configuration Redis: le fichier de configuration redis est généralement situé dans /etc/redis/redis.conf. Edit Fichier de configuration: Ouvrez le fichier de configuration à l'aide d'un éditeur de texte (tel que VI ou NANO): Sudovi / etc / redis / redis.conf Définissez le délai d'exécution du script LUA: Ajouter ou modifier les lignes suivantes dans le fichier de configuration pour définir le temps d'exécution maximal du script LUA (unité: millisecondes)

Utilisez l'outil de ligne de commande redis (Redis-CLI) pour gérer et utiliser Redis via les étapes suivantes: Connectez-vous au serveur, spécifiez l'adresse et le port. Envoyez des commandes au serveur à l'aide du nom et des paramètres de commande. Utilisez la commande d'aide pour afficher les informations d'aide pour une commande spécifique. Utilisez la commande QUIT pour quitter l'outil de ligne de commande.

Dans Debian Systems, les appels du système ReadDir sont utilisés pour lire le contenu des répertoires. Si ses performances ne sont pas bonnes, essayez la stratégie d'optimisation suivante: simplifiez le nombre de fichiers d'annuaire: divisez les grands répertoires en plusieurs petits répertoires autant que possible, en réduisant le nombre d'éléments traités par appel ReadDir. Activer la mise en cache de contenu du répertoire: construire un mécanisme de cache, mettre à jour le cache régulièrement ou lorsque le contenu du répertoire change et réduire les appels fréquents à Readdir. Les caches de mémoire (telles que Memcached ou Redis) ou les caches locales (telles que les fichiers ou les bases de données) peuvent être prises en compte. Adoptez une structure de données efficace: si vous implémentez vous-même la traversée du répertoire, sélectionnez des structures de données plus efficaces (telles que les tables de hachage au lieu de la recherche linéaire) pour stocker et accéder aux informations du répertoire
