Il existe de nombreuses façons d'implémenter des verrous distribués. Cet article décrit l'utilisation de Redis pour implémenter des verrous distribués. Il existe de nombreux codes sur Internet qui utilisent Redis pour implémenter des verrous distribués, mais ces codes sont plus ou moins problématiques. Cet article rédigera une implémentation et indiquera également quelques points d'attention.
Scènes
Pour faciliter l'explication, voici un scénario de jeu où l'utilisateur A a une hache de montagne d'une valeur de 500 lingots et l'utilisateur B a 800 lingots et veut acheter une hache de montagne A. Ces données sont stockées dans Redis. Vous devez écrire du code pour implémenter avec succès la transaction.
problème
Redis implémente les verrous distribués et doit prendre en compte les problèmes suivants:
- Le processus qui maintient le verrou provoque la libération automatique du verrou car le temps de fonctionnement est trop long, mais le processus lui-même ne le sait pas et peut même libérer les verrous détenus par d'autres processus par erreur.
- Un processus détenant un verrou et ayant l'intention d'effectuer une longue opération s'est écrasé, mais d'autres processus qui veulent acquérir le verrou ne savent pas quel processus détient le verrou, ni ne peuvent détecter que le processus qui détient le verrou s'est écrasé et ne peut perdre du temps qu'en vain Attendez que le verrou soit libéré.
- Une fois le verrou détenu par un processus expiré, plusieurs autres processus tentent d'acquérir le verrou en même temps et tous acquièrent le verrou.
Trois caractéristiques
Pour implémenter une serrure distribuée avec une garantie minimale, trois caractéristiques sont requises
- Propriété de sécurité: Exclusif (mutuellement exclusif). À tout moment, un seul client détient le verrou.
- Propriété de vitalité A: aucune impasse. Même si le client qui détient le verrou tombe en panne ou si le réseau est partitionné, le verrou peut toujours être acquis.
- Propriété de vivacité B: tolérance aux pannes. Tant que la plupart des nœuds Redis sont actifs, le client peut acquérir et libérer le verrou.
commander
Utilisez Redis pour implémenter des verrous distribués, utilisez généralement les commandes SETNX ou SET. SETNX ne peut pas définir l'heure d'expiration en même temps. Si la version utilisée est supérieure ou égale à 2.6.12, vous pouvez utiliser la commande SET, qui peut être utilisée pour réaliser les fonctions de SETNX et EXPIRE de manière atomique. Vous trouverez ci-dessous une brève introduction aux deux commandes
SETNX
Format de la commande: valeur de clé SETNX
Complexité temporelle: O (1)
Remarque: key
définissez la valeur value
, si elle key
n'existe pas, elle équivaut à la commande SET dans ce cas . Quand cela key
existe, ne faites rien. SETNX
C'est le raccourci " SET IF N OT E X- IST".
valeur de retour
1
Si la clé est définie0
Si la clé n'est pas définie
ENSEMBLE
Format de la commande: valeur de la clé SET [EX secondes] [PX millisecondes] [NX | XX]
Complexité temporelle: O (1)
Description: définissez la clé key
sur la valeur "chaîne" spécifiée. Si la clé a enregistré une valeur, cette opération écrasera directement la valeur d'origine et ignorera le type d'origine. Lorsque la set
commande est exécutée avec succès, le délai d'expiration défini précédemment sera invalide.
Options
À partir de la version 2.6.12, redis a SET
ajouté une série d'options à la commande:
EX
secondes -Réglez l'heure d'expiration de la clé, en heures et secondesPX
millisecondes - Définit le délai d'expiration de la clé, en millisecondesNX
-La valeur de la clé ne sera définie que lorsque la clé n'existe pasXX
-Seule la valeur de la clé sera définie lorsque la clé existe
atteindre
SETNX est utilisé ici. Après tout, certaines entreprises peuvent avoir une version inférieure de Redis, qui peut être réalisée en utilisant SETNX, et SET est encore plus sans problème.
code montrer comme ci-dessous:
<?php
function uuid($prefix = '')
{
$chars = md5(uniqid(mt_rand(), true));
$uuid = substr($chars, 0, 8) . '-';
$uuid .= substr($chars, 8, 4) . '-';
$uuid .= substr($chars, 12, 4) . '-';
$uuid .= substr($chars, 16, 4) . '-';
$uuid .= substr($chars, 20, 12);
$ret = $prefix . $uuid;
return strtoupper($ret);
}
function acquireLock($redis,$lockName, $acquireTime = 10, $lockTime = 10)
{
$lockKey = 'lock:' + $lockName;
$identifier = uuid('identify');
$end = time() + $acquireTime;
while (time() < $end) {
if ($redis->setnx($lockKey, $identifier)) {
$redis->expire($lockKey, $lockTime);
return $identifier;
} elseif ($redis->ttl($lockKey) == -1) {
$redis->expire($lockKey, $lockTime);
}
usleep(1000);
}
return false;
}
function process(){
$redis = new Redis();
$lockName = 'market';
//1.获取锁
$locked = acquireLock($redis,$lockName);
if($locked === false){
return false;
}
//2.进行交易
//判断A和B是否满足交易条件
//使用管道,对A和B进行操作
//3.释放锁
$releaseRes = releaseLock($redis,$lockName,$locked);
if($releaseRes === false){
return false;
}
}
function releaseLock($redis,$lockName,$identifier){
$lockKey = 'lock:' + $lockName;
$redis->watch($lockKey);
if($redis->get($lockKey) === $identifier){
$redis->multi();
$redis->del($lockKey);
$redis->exec();
return true;
}
$redis->unwatch();
return false;
}
La description:
- Acquérir la serrure:
- Créer un identifiant $ unique, cette valeur est utilisée pour déterminer si le verrou est acquis par le client actuel lors de la suppression de la clé, afin de ne pas supprimer les verrous des autres clients
- La boucle while est utilisée pour continuer à acquérir le verrou pendant un certain temps
- Si vous pouvez obtenir le verrou, vous pouvez l'obtenir et définir un délai pour éviter que le thread ne se bloque lors de son exécution. Le verrou ne peut jamais être libéré.
- Si le verrou ne peut pas être acquis avec succès, vérifiez le délai d'expiration du verrou actuel. Si le délai d'expiration n'est pas défini, définissez-le pour empêcher les autres threads de se bloquer immédiatement après l'acquisition du verrou. Aucun délai d'expiration n'est défini
- Gérer les affaires
- Vous devez d'abord déterminer si les deux parties à la transaction remplissent les conditions, car l'ensemble du marché est verrouillé, donc une fois le verrou obtenu, le statut des deux parties ne changera pas.
- L'utilisation du pipeline peut garantir que l'ensemble de la transaction peut être traitée comme une transaction et que les performances seront meilleures que la transaction utilisant redis
- Verrouillage de libération
- Utilisez la montre pour surveiller la serrure. Une fois la clé modifiée, la transaction qui supprime la clé ne sera pas exécutée
- Besoin de déterminer si la valeur de la clé est la même que l'identifiant $ enregistré par ce thread, et ce n'est que si elle est cohérente qu'elle peut être supprimée
- Utilisez des transactions pour supprimer des clés. La raison de l'utilisation des transactions est d'empêcher le verrou d'être acquis par d'autres threads.
- En cas d'échec, n'oubliez pas de déballer
- d'autres problèmes
- Problème de réentrant: réentrant signifie que le thread peut à nouveau acquérir le verrou. La méthode d'implémentation est relativement simple. Il vous suffit de transmettre l'identifiant lors de l'acquisition deLock pour déterminer si l'identifiant du verrou actuel est cohérent avec celui transmis. S'ils sont cohérents, l'opération peut être effectuée
- Le thread n'a pas été exécuté, le délai de verrouillage est passé et d'autres threads ont acquis le verrou: une solution à ce problème est de vérifier si le verrou existe ou a été modifié après la moitié du délai d'expiration, s'il n'y a pas de changement. Et le thread fonctionne normalement, prolongez le délai d'expiration
En pensant
Si elle est basée sur une seule instance Redis, en supposant que cette instance unique est toujours disponible, cette méthode est suffisamment sûre.
Mais il y a deux situations particulières auxquelles tout le monde doit prêter attention:
Il existe des conditions de concurrence évidentes dans la structure maître-esclave:
- Le client A obtient le verrou du maître
- Avant que le maître synchronise le verrou avec l'esclave, le maître tombe en panne.
- Le nœud esclave est promu nœud maître
- Le client B acquiert un autre verrou pour la même ressource que le client A a déjà acquise. Échec de sécurité!
Dans l'environnement distribué de Redis, il existe N maîtres Redis
Dans ce cas, l' algorithme Redlock peut être utilisé
Pour résumer
Cet article décrit comment utiliser Redis pour implémenter des verrous distribués et écrit une implémentation spécifique et l'analyse associée. Pour utiliser Redis pour implémenter des verrous distribués, de nombreux détails doivent être pris en compte: vous pouvez concevoir des verrous qui répondent à vos besoins en fonction de votre propre fiche métier et faire un compromis entre complexité et sécurité.
Les données
- https://www.jianshu.com/p/bb8c6c3113dd
- http://redis.cn/topics/distlock.html
- http://redis.cn/commands/set.html
Enfin
Si vous aimez mon article, vous pouvez suivre mon compte public (Programmeur Mala Tang)
Revue des articles précédents:
- Redis implémente les verrous distribués
- Suivi des bogues du code source de Golang
- Le principe de réalisation de l'atomicité, de la cohérence et de la durabilité des transactions
- Comment exercer votre mémoire
- Explication détaillée du processus de demande de CDN
- Réflexions sur le développement de carrière des programmeurs
- L'histoire du service de blog écrasée
- Techniques de mise en cache courantes
- Comment se connecter efficacement avec un paiement tiers
- Version concise du cadre Gin
- Réflexion sur la révision du code
- Une brève analyse des verrous et des transactions InnoDB
- Éditeur Markdown recommandation-typora