Maison > Article > base de données > Parlons du principe des verrous distribués et de la façon dont Redis implémente les verrous distribués
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.
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 :
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 :
SET resource_name my_random_value NX PX 30000
Version 1
1 Définir l'interface ILockpublic interface ILock extends AutoCloseable {
/**
* 尝试获取锁
*
* @param timeoutSec 锁持有的超时时间,过期后自动释放
* @return true代表获取锁成功;false代表获取锁失败
*/
boolean tryLock(long timeoutSec);
/**
* 释放锁
* @return
*/
void unLock();
}
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() {
//通过del删除锁
stringRedisTemplate.delete(KEY_PREFIX + name);
}
@Override
public void close() {
unLock();
}
}
Problème de suppression accidentelle du verrou
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
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(); } }
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é
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"; //通过执行lua脚本实现锁删除,可以校验随机值 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(); } }
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!