Cet article vous apporte des connaissances pertinentes sur Redis, qui présente principalement ce que sont les verrous distribués ? Comment Redis implémente-t-il les verrous distribués ? Quelles conditions doivent être remplies ? Jetons un coup d'œil ci-dessous, j'espère que cela sera utile aux amis dans le besoin.
1. Principes de base des verrous distribués
Verrous distribués : verrous visibles et mutuellement exclusifs à plusieurs processus dans un système distribué ou en mode cluster.
Conditions que les verrous distribués doivent remplir :
- Visibilité : plusieurs threads peuvent voir le même résultat. Remarque : la visibilité mentionnée ici n'est pas la visibilité de la mémoire dans la programmation simultanée. Cela signifie simplement que plusieurs processus peuvent percevoir les changements. Exclusion mutuelle : l'exclusion mutuelle est la condition la plus fondamentale pour la distribution. verrous, permettant au programme de s'exécuter en série
- Haute disponibilité : le programme n'est pas facile à planter, garantissant une haute disponibilité à tout moment
- Haute performance : en raison du verrou lui-même, les performances sont réduites, tous les verrous distribués eux-mêmes nécessitent des performances de verrouillage plus élevées et performances de libération des verrous
- Sécurité : La sécurité est également une partie essentielle du programme
-
Il existe trois verrous distribués courants :
Mysql : Mysql lui-même a un mécanisme de verrouillage, mais en raison des performances moyennes de MySQL , lors de l'utilisation de verrous distribués, il est en fait relativement rare d'utiliser mysql comme verrou distribué Redis : redis comme verrou distribué est C'est une façon très courante de l'utiliser de nos jours, redis ou zookeeper sont essentiellement utilisés comme distribués. verrouille dans le développement au niveau de l'entreprise. En utilisant la méthode setnx, si la clé est insérée avec succès, cela signifie que le verrou est obtenu. Si quelqu'un l'insère avec succès, d'autres ne parviennent pas à l'insérer. Indique que le verrou ne peut pas être obtenu. pour implémenter des verrous distribués Zookeeper : zookeeper est également une meilleure solution pour implémenter des verrous distribués dans le développement au niveau de l'entreprise
2. Implémenter la distribution basée sur le verrouillage de type Redis
Deux méthodes de base qui doivent être implémenté lors de l'implémentation de verrous distribués :
Acquisition du verrou :-
Exclusion mutuelle : garantir qu'un seul thread peut acquérir le verrou
- Non bloquant : essayez une fois et renvoyez vrai avec succès, renvoyez faux en cas d'échec
-
Release lock : -
Release manuelle
- Timeout release : ajoutez un timeout lors de l'acquisition du verrou
-
Implémentez le principe du verrouillage distribué basé sur Redis :
1 | <strong>SET resource_name my_random_value NX PX 30000</strong>
|
Copier après la connexion
resource_name : Nom de la ressource, différents verrous peuvent être distingué selon les différentes entreprises
- my_random_value : valeur aléatoire, la valeur aléatoire de chaque thread est différente, utilisée pour la vérification lors du déverrouillage
- NX : définie avec succès lorsque la clé n'existe pas, la clé existe Ensuite, le réglage échoue
- PX : délai d'expiration automatique, si une exception se produit, le verrou peut expirer et expirer
- En utilisant l'atomicité de NX, lorsque plusieurs threads sont simultanés, un seul thread peut être défini avec succès signifie que le verrou est obtenu. et peut être exécuté. Traitement commercial ultérieur ; si une exception se produit et que le verrou expire, le verrou est automatiquement libéré
Version 1
1 Définir l'interface ILock
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <strong> public interface ILock extends AutoCloseable {
boolean tryLock(long timeoutSec);
void unLock();
}</strong>
|
Copier après la connexion
2. - RedisLock
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <strong> public class SimpleRedisLock {
private final StringRedisTemplate stringRedisTemplate;
private final String name;
public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {
this.stringRedisTemplate = stringRedisTemplate;
this.name = name;
}
private static final String KEY_PREFIX = "lock:" ;
@Override
public boolean tryLock(long timeoutSec) {
String threadId = Thread.currentThread().getId();
Boolean success = stringRedisTemplate.opsForValue()
.setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
return Boolean.TRUE.equals(success);
}
@Override
public void unLock() {
stringRedisTemplate. delete (KEY_PREFIX + name);
}
@Override
public void close() {
unLock();
}
}</strong>
|
Copier après la connexion
Problème de suppression accidentelle du verrou
Description du problème :
Le thread 1 tenant le verrou est bloqué à l'intérieur du verrou. À ce moment, le verrou est automatiquement libéré après l'expiration du délai. À ce moment, le thread 2 essaie de le faire. obtenez le verrou, puis le thread 2 s'exécute tout en maintenant le verrou. Pendant le processus, le thread 1 réagit, continue l'exécution et atteint la logique de suppression du verrou. À ce moment, le verrou qui devrait appartenir au thread 2 sera supprimé. le cas d'une suppression accidentelle du verrou.
Solution :
Lors du stockage du verrou, mettez l'ID de votre propre fil de discussion. Lors de la suppression du verrou, jugez si l'ID du verrou actuel est celui que vous avez enregistré. Si tel est le cas, supprimez-le. it , aucune suppression ne sera effectuée.
Version 2 : Résoudre le problème de la suppression accidentelle du verrou
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | public class SimpleRedisLock {
private final StringRedisTemplate stringRedisTemplate;
private final String name;
public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {
this.stringRedisTemplate = stringRedisTemplate;
this.name = name;
}
private static final String KEY_PREFIX = "lock:" ;
private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-" ;
@Override
public boolean tryLock(long timeoutSec) {
String threadId = ID_PREFIX + Thread.currentThread().getId();
Boolean success = stringRedisTemplate.opsForValue()
.setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
return Boolean.TRUE.equals(success);
}
@Override
public void unLock() {
String threadId = ID_PREFIX + Thread.currentThread().getId();
String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
if (threadId.equals(id)) {
stringRedisTemplate. delete (KEY_PREFIX + name);
}
}
@Override
public void close() {
unLock();
}
}
|
Copier après la connexion
Le problème d'atomicité de la libération du verrou
Analyse du problème :
Le code ci-dessus pour libérer le verrou présente toujours le problème de la suppression accidentelle du verrou lorsque le thread 1 est obtenu. l'ID du thread dans le verrou, Et sur la base de l'identification, on estime qu'il s'agit de son propre verrou. À ce moment-là, le verrou est automatiquement libéré à son expiration. Il arrive que le thread 2 tente d'acquérir le verrou et l'obtienne. . A ce moment, le thread 1 effectue toujours l'opération de libération du verrou, ce qui entraîne la suppression accidentelle du verrou détenu par le thread 2. Lock.
La raison est que le processus de déverrouillage implémenté par le code Java n'est pas une opération atomique et présente des problèmes de sécurité des threads.
Solution :
Redis fournit la fonction de script Lua. L'écriture de plusieurs commandes Redis dans un seul script peut garantir l'atomicité de l'exécution de plusieurs commandes.
Version 3 : Appelez le script Lua pour transformer le verrouillage distribué
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | public class SimpleRedisLock implements ILock {
private final StringRedisTemplate stringRedisTemplate;
private final String name;
public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {
this.stringRedisTemplate = stringRedisTemplate;
this.name = name;
}
private static final String KEY_PREFIX = "lock:" ;
private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-" ;
@Override
public boolean tryLock(long timeoutSec) {
String threadId = ID_PREFIX + Thread.currentThread().getId();
Boolean success = stringRedisTemplate.opsForValue()
.setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
return Boolean.TRUE.equals(success);
}
@Override
public void unLock() {
String script = "if redis.call(" get ",KEYS[1]) == ARGV[1] then\n" +
" return redis.call(" del ",KEYS[1])\n" +
"else\n" +
" return 0\n" +
"end" ;
RedisScript<Boolean> redisScript = RedisScript.of(script, Boolean. class );
stringRedisTemplate.execute(redisScript,
Collections.singletonList(KEY_PREFIX + name),
ID_PREFIX + Thread.currentThread().getId());
}
@Override
public void close() {
unLock();
}
}
|
Copier après la connexion
Apprentissage recommandé : "
Tutoriel vidéo Redis
"
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!