Table des matières
Analyse et mise en œuvre
Peut-être que de nombreux étudiants (moi y compris) utilisent la méthode de mise en œuvre ci-dessus dans leur travail quotidien, qui semble sûre :
Selon le document officiel, l'idée de conception de RedLock est introduite.
代码实现
全文总结
Maison base de données Redis Utilisez Redis pour implémenter un verrou distribué sûr et fiable

Utilisez Redis pour implémenter un verrou distribué sûr et fiable

Apr 19, 2021 am 10:20 AM
redis 分布式锁

Cet article vous présentera comment utiliser Redis pour implémenter un verrou distribué sûr et fiable, et expliquera les principaux éléments et les malentendus courants concernant la mise en œuvre du verrou distribué. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il sera utile à tout le monde.

Utilisez Redis pour implémenter un verrou distribué sûr et fiable

Dans un scénario simultané, lorsque plusieurs processus ou threads partagent des ressources de lecture et d'écriture, l'accès aux ressources doit être garanti pour s'exclure mutuellement. Dans un système autonome, nous pouvons utiliser l'API du package de concurrence Java, le mot-clé synchronisé, etc. pour résoudre le problème, mais dans un système distribué, ces méthodes ne sont plus applicables et nous devons implémenter nous-mêmes les verrous distribués ; .

Les solutions courantes de mise en œuvre de verrous distribués incluent : basées sur une base de données, basées sur Redis, basées sur Zookeeper, etc. Dans le cadre du sujet Redis, cet article parlera de la mise en œuvre de verrous distribués basés sur Redis. [Recommandations associées : Tutoriel vidéo Redis]

Analyse et mise en œuvre


Analyse des problèmes

Les verrous distribués et les verrous intégrés JVM ont le même objectif : permettre aux applications d'accéder ou de faire fonctionner des ressources partagées dans l'ordre prévu, et d'empêcher plusieurs threads de fonctionner sur la même ressource en même temps, provoquant le système fonctionne de manière désordonnée et incontrôlable. Souvent utilisé dans des scénarios tels que les déductions sur les stocks de produits et les déductions sur les coupons.

Théoriquement, afin de garantir la sécurité et l'efficacité de la serrure, les serrures distribuées doivent remplir au moins les conditions suivantes :

  • Exclusivité mutuelle : en même temps, il existe un seul Le thread peut acquérir le verrou ;
  • Pas de blocage : une fois que le thread a acquis le verrou, il doit être garanti de pouvoir le libérer. Même si l'application tombe en panne après que le thread a acquis le verrou, il doit être garanti. peut être libéré dans un temps limité ;
  • Ajouté Le verrouillage et le déverrouillage doivent être dans le même fil

En termes de mise en œuvre, les verrous distribués sont grossièrement divisés en trois étapes :

  • a- Obtenir le droit d'exploiter la ressource
  • b - Effectuer des opérations sur les ressources
  • c - Libérer les droits d'exploitation des ressources ; >
  • Qu'il s'agisse du verrou intégré de Java ou du verrou distribué, peu importe. La solution d'implémentation distribuée utilisée dépend des deux étapes a et c. Redis est naturellement convivial pour implémenter des verrous distribués pour les raisons suivantes :

Pendant la phase de traitement des commandes, Redis utilise un traitement monothread. Un seul thread peut traiter la même clé en même temps, donc là. il n'y a pas de problème de condition de concurrence multithread. La commande
  • ajoute une clé avec un délai d'expiration si la clé n'existe pas, fournissant ainsi la prise en charge du verrouillage de sécurité.
  • SET key value NX PX millisecondsLes scripts Lua et les commandes DEL fournissent un support fiable pour un déverrouillage en toute sécurité.
Implémentation du code

Dépendance Maven
  • <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
      	<version>${your-spring-boot-version}</version>
    </dependency>
    Copier après la connexion
Fichier de configuration
  • Ajoutez le contenu suivant dans application.properties, instance Redis autonome.
spring.redis.database=0
spring.redis.host=localhost
spring.redis.port=6379
Copier après la connexion

RedisConfig
  • @Configuration
    public class RedisConfig {
    
        // 自己定义了一个 RedisTemplate
        @Bean
        @SuppressWarnings("all")
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory)
            throws UnknownHostException {
            // 我们为了自己开发方便,一般直接使用 <String, Object>
            RedisTemplate<String, Object> template = new RedisTemplate<String,
                Object>();
            template.setConnectionFactory(factory);
            // Json序列化配置
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            // String 的序列化
            StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
            // key采用String的序列化方式
            template.setKeySerializer(stringRedisSerializer);
            // hash的key也采用String的序列化方式
            template.setHashKeySerializer(stringRedisSerializer);
            // value序列化方式采用jackson
            template.setValueSerializer(jackson2JsonRedisSerializer);
            // hash的value序列化方式采用jackson
            template.setHashValueSerializer(jackson2JsonRedisSerializer);
            template.afterPropertiesSet();
            return template;
        }
    }
    Copier après la connexion
RedisLock
  • @Service
    public class RedisLock {
    
        @Resource
        private RedisTemplate<String, Object> redisTemplate;
    
        /**
         * 加锁,最多等待maxWait毫秒
         *
         * @param lockKey   锁定key
         * @param lockValue 锁定value
         * @param timeout   锁定时长(毫秒)
         * @param maxWait   加锁等待时间(毫秒)
         * @return true-成功,false-失败
         */
        public boolean tryAcquire(String lockKey, String lockValue, int timeout, long maxWait) {
            long start = System.currentTimeMillis();
    
            while (true) {
                // 尝试加锁
                Boolean ret = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, timeout, TimeUnit.MILLISECONDS);
                if (!ObjectUtils.isEmpty(ret) && ret) {
                    return true;
                }
    
                // 计算已经等待的时间
                long now = System.currentTimeMillis();
                if (now - start > maxWait) {
                    return false;
                }
    
                try {
                    Thread.sleep(200);
                } catch (Exception ex) {
                    return false;
                }
            }
        }
    
        /**
         * 释放锁
         *
         * @param lockKey   锁定key
         * @param lockValue 锁定value
         * @return true-成功,false-失败
         */
        public boolean releaseLock(String lockKey, String lockValue) {
            // lua脚本
            String script = "if redis.call(&#39;get&#39;,KEYS[1]) == ARGV[1] then return redis.call(&#39;del&#39;,KEYS[1]) else return 0 end";
    
            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
            Long result = redisTemplate.opsForValue().getOperations().execute(redisScript, Collections.singletonList(lockKey), lockValue);
            return result != null && result > 0L;
        }
    }
    Copier après la connexion
Cas de test
  • @SpringBootTest
    class RedisDistLockDemoApplicationTests {
    
        @Resource
        private RedisLock redisLock;
    
        @Test
        public void testLock() {
            redisLock.tryAcquire("abcd", "abcd", 5 * 60 * 1000, 5 * 1000);
            redisLock.releaseLock("abcd", "abcd");
        }
    }
    Copier après la connexion
Risques pour la sécurité

Peut-être que de nombreux étudiants (moi y compris) utilisent la méthode de mise en œuvre ci-dessus dans leur travail quotidien, qui semble sûre :

Utiliser
    Commandez les options
  • et set pour verrouiller, garantissant l'exclusion mutuelle des verrous et évitant les blocages ; NXPXUtilisez le script Lua pour déverrouiller afin d'empêcher le déverrouillage d'autres threads
  • Commandes de verrouillage et de déverrouillage ; sont toutes des opérations atomiques ;
  • En fait, il y a des conditions préalables pour que l'implémentation ci-dessus soit stable : version autonome de Redis, activer le mode de persistance AOF et configurer
.

appendfsync=alwaysMais il peut y avoir des problèmes en mode sentinelle et en mode cluster, pourquoi ?

Le mode Sentinelle et le mode cluster sont basés sur l'architecture maître-esclave. La synchronisation des données est réalisée entre le maître et l'esclave via la propagation des commandes, et la propagation des commandes est asynchrone.

Il est donc possible que les données du nœud maître soient écrites avec succès, mais que le nœud maître tombe en panne avant que le nœud esclave ne soit notifié.

Lorsque le nœud esclave est promu au nouveau nœud maître via le basculement, les autres threads ont la possibilité de se reverrouiller avec succès, ce qui fait que la condition d'exclusion mutuelle du verrou distribué n'est pas remplie.

RedLock officiel

En mode cluster, si tous les nœuds du cluster fonctionnent de manière stable et que le basculement ne se produit pas, la sécurité est garantie. Cependant, aucun système ne peut garantir une stabilité à 100 % et les verrous distribués basés sur Redis doivent prendre en compte la tolérance aux pannes.


La synchronisation maître-esclave étant basée sur le principe de la réplication asynchrone, le mode sentinelle et le mode cluster sont intrinsèquement incapables de remplir cette condition. Pour cette raison, l'auteur de Redis a spécialement proposé une solution-RedLock (Redis Distribute Lock).

Idée de conception

Selon le document officiel, l'idée de conception de RedLock est introduite.

Parlons d'abord des exigences de l'environnement. N (N>=3) instances Redis déployées indépendamment sont requises. La réplication maître-esclave, le basculement et d'autres technologies ne sont pas requis entre eux.

Afin d'obtenir le cadenas, le client suivra le processus suivant :

  • 获取当前时间(毫秒)作为开始时间start;
  • 使用相同的key和随机value,按顺序向所有N个节点发起获取锁的请求。当向每个实例设置锁时,客户端会使用一个过期时间(小于锁的自动释放时间)。比如锁的自动释放时间是10秒,这个超时时间应该是5-50毫秒。这是为了防止客户端在一个已经宕机的实例浪费太多时间:如果Redis实例宕机,客户端尽快处理下一个实例。
  • 客户端计算加锁消耗的时间cost(cost=start-now)。只有客户端在半数以上实例加锁成功,并且整个耗时小于整个有效时间(ttl),才能认为当前客户端加锁成功。
  • 如果客户端加锁成功,那么整个锁的真正有效时间应该是:validTime=ttl-cost。
  • 如果客户端加锁失败(可能是获取锁成功实例数未过半,也可能是耗时超过ttl),那么客户端应该向所有实例尝试解锁(即使刚刚客户端认为加锁失败)。

RedLock的设计思路延续了Redis内部多种场景的投票方案,通过多个实例分别加锁解决竞态问题,虽然加锁消耗了时间,但是消除了主从机制下的安全问题。

代码实现

官方推荐Java实现为Redisson,它具备可重入特性,按照RedLock进行实现,支持独立实例模式、集群模式、主从模式、哨兵模式等;API比较简单,上手容易。示例如下(直接通过测试用例):

    @Test
    public void testRedLock() throws InterruptedException {

        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        final RedissonClient client = Redisson.create(config);

        // 获取锁实例
        final RLock lock = client.getLock("test-lock");

        // 加锁
        lock.lock(60 * 1000, TimeUnit.MILLISECONDS);
        try {
            // 假装做些什么事情
            Thread.sleep(50 * 1000);
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }
    }
Copier après la connexion

Redisson封装的非常好,我们可以像使用Java内置的锁一样去使用,代码简洁的不能再少了。关于Redisson源码的分析,网上有很多文章大家可以找找看。

全文总结


分布式锁是我们研发过程中常用的的一种解决并发问题的方式,Redis是只是一种实现方式。

关键的是要弄清楚加锁、解锁背后的原理,以及实现分布式锁需要解决的核心问题,同时考虑我们所采用的中间件有什么特性可以支撑。了解这些后,实现起来就不是什么问题了。

学习了RedLock的思想,我们是不是也可以在自己的应用程序内实现了分布式锁呢?欢迎沟通!

更多编程相关知识,请访问:编程入门!!

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 de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

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

Comment construire le mode Cluster Redis Comment construire le mode Cluster Redis Apr 10, 2025 pm 10:15 PM

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 Comment effacer les données redis Apr 10, 2025 pm 10:06 PM

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.

Comment utiliser la commande redis Comment utiliser la commande redis Apr 10, 2025 pm 08:45 PM

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).

Comment utiliser Redis Lock Comment utiliser Redis Lock Apr 10, 2025 pm 08:39 PM

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.

Comment lire la file d'attente redis Comment lire la file d'attente redis Apr 10, 2025 pm 10:12 PM

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.

Comment implémenter le redis sous-jacent Comment implémenter le redis sous-jacent Apr 10, 2025 pm 07:21 PM

Redis utilise des tables de hachage pour stocker les données et prend en charge les structures de données telles que les chaînes, les listes, les tables de hachage, les collections et les collections ordonnées. Redis persiste les données via des instantanés (RDB) et ajoutez les mécanismes d'écriture uniquement (AOF). Redis utilise la réplication maître-esclave pour améliorer la disponibilité des données. Redis utilise une boucle d'événement unique pour gérer les connexions et les commandes pour assurer l'atomicité et la cohérence des données. Redis définit le temps d'expiration de la clé et utilise le mécanisme de suppression paresseux pour supprimer la clé d'expiration.

Comment lire le code source de Redis Comment lire le code source de Redis Apr 10, 2025 pm 08:27 PM

La meilleure façon de comprendre le code source redis est d'aller étape par étape: familiarisez-vous avec les bases de Redis. Sélectionnez un module ou une fonction spécifique comme point de départ. Commencez par le point d'entrée du module ou de la fonction et affichez le code ligne par ligne. Affichez le code via la chaîne d'appel de fonction. Familiez les structures de données sous-jacentes utilisées par Redis. Identifiez l'algorithme utilisé par Redis.

Comment faire du message middleware pour redis Comment faire du message middleware pour redis Apr 10, 2025 pm 07:51 PM

Redis, en tant que Message Middleware, prend en charge les modèles de consommation de production, peut persister des messages et assurer une livraison fiable. L'utilisation de Redis comme Message Middleware permet une faible latence, une messagerie fiable et évolutive.

See all articles