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

Parlons du principe des verrous distribués et de la façon dont Redis implémente les verrous distribués

藏色散人
藏色散人avant
2023-01-27 07:30:011056parcourir

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

Parlons du principe des verrous distribués et de la façon dont Redis implémente les verrous distribués 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 :

SET resource_name my_random_value NX PX 30000

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

public interface ILock extends AutoCloseable {
    /**
     * 尝试获取锁
     *
     * @param timeoutSec 锁持有的超时时间,过期后自动释放
     * @return true代表获取锁成功;false代表获取锁失败
     */
    boolean tryLock(long timeoutSec);

    /**
     * 释放锁
     * @return
     */
    void unLock();
}

2. - RedisLock

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

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

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 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é

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();
    }
}

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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer