redis红锁Redlock

redis红锁Redlock

文档

  1. redis单机安装
  2. redis常用的五种数据类型
  3. springboot整合redis-RedisTemplate单机模式
  4. 布隆过滤器 -Bloom Filter

官方文档

  1. 官网操作命令指南页面:https://redis.io/docs/latest/commands/?name=get&group=string
  2. Redis cluster specification
  3. Distributed Locks with Redis

说明

  1. redis版本:7.0.0
  2. springboot版本:3.2.0
  3. redisson的版本号:3.37.0

redis红锁Redlock

安装单机版redis
  1. 安装单机版redis参考文档:redis单机安装
Redlock官方文档
  1. Redlock官方文档地址:https://redis.io/docs/latest/develop/clients/patterns/distributed-locks/#the-redlock-algorithm

  2. 文档英文原文

    The Redlock Algorithm

    In the distributed version of the algorithm we assume we have N Redis masters. Those nodes are totally independent, so we don't use replication or any other implicit coordination system. We already described how to acquire and release the lock safely in a single instance. We take for granted that the algorithm will use this method to acquire and release the lock in a single instance. In our examples we set N=5, which is a reasonable value, so we need to run 5 Redis masters on different computers or virtual machines in order to ensure that they'll fail in a mostly independent way.

    In order to acquire the lock, the client performs the following operations:

    1. It gets the current time in milliseconds.
    2. It tries to acquire the lock in all the N instances in parallel, using the same key name and random value in all the instances. During step 2, when setting the lock in each instance, the client uses a timeout which is small compared to the total lock auto-release time in order to acquire it. For example if the auto-release time is 10 seconds, the timeout could be in the ~ 5-50 milliseconds range. This prevents the client from staying blocked too long when communicating with an unavailable Redis node, ensuring the connection attempt times out quickly.
    3. The client computes how much time elapsed in order to acquire the lock, by subtracting from the current time the timestamp obtained in step 1. If and only if the client was able to acquire the lock in the majority of the instances (at least 3), and the total time elapsed to acquire the lock is less than lock validity time, the lock is considered to be acquired.
    4. If the lock was acquired, its validity time is considered to be the initial validity time minus the time elapsed, as computed in step 3.
    5. If the client failed to acquire the lock for some reason (either it was not able to lock N/2+1 instances or the validity time is negative), it will try to unlock all the instances (even the instances it believed it was not able to lock).
  3. 翻译

    Redlock 算法

    在该算法的分布式版本中,我们假设有 N 个 Redis 主节点(master)。这些节点彼此完全独立,因此我们不使用主从复制或任何隐式协调系统。我们已经说明了在单实例中如何安全地获取和释放锁。这里默认分布式算法在每个单实例上都采用同样的方法来加锁和解锁。示例中取 N=5(这是一个合理值),因此需要在不同的机器或虚拟机上部署 5 个 Redis 主节点,以保证它们的故障大体上相互独立。

    为了获取锁,客户端执行以下操作:

    1. 获取当前时间(毫秒)。
    2. 在所有 N 个实例上并行尝试加锁,使用相同的 key 名和相同的随机值。在这一步里,客户端给每个实例设置锁时,都会使用一个相对于锁自动释放时间来说较小的超时时间。例如,若锁自动释放时间是 10 秒,则该超时可以在约 5~50 毫秒范围内。这样可以避免客户端在与不可用 Redis 节点通信时阻塞过久,确保连接尝试能快速超时。
    3. 客户端计算获取锁所花费的时间:用当前时间减去步骤 1 的时间戳。只有当客户端在大多数实例(至少 3 个)上成功加锁,且总耗时小于锁的有效期时,才认为加锁成功。
    4. 如果加锁成功,锁的有效期应视为:初始有效期 - 步骤 3 计算出的耗时。
    5. 如果客户端因为任何原因加锁失败(要么成功实例数不足 N/2 + 1,要么剩余有效期为负),它会尝试在所有实例上执行解锁(包括那些它认为自己可能没加锁成功的实例)。
Redisson实现Redlock
  1. 在Springboot中,为了 RedLock 配置多个独立的RedissonClient

  2. 修改配置文件application.yml,配redis连接信息,注意redlock使用的redis节点,应彼此完全独立

    yaml 复制代码
    app:
      redisson:
        nodes:
          n1:
            address: redis://127.0.0.1:6379
            password:
            database: 0
            timeout: 3000
          n2:
            address: redis://127.0.0.1:6380
            password:
            database: 0
            timeout: 3000
          n3:
            address: redis://127.0.0.1:6381
            password:
            database: 0
            timeout: 3000
    • address 必须带协议: redis:// rediss://
  3. 配置属性类(读取配置文件)

    java 复制代码
    @Configuration
    @ConfigurationProperties(prefix = "app.redisson")
    public class MultiRedissonProperties {
    
        private Map<String, Node> nodes = new HashMap<>();
    
        public Map<String, Node> getNodes() {
            return nodes;
        }
    
        public void setNodes(Map<String, Node> nodes) {
            this.nodes = nodes;
        }
    
        public static class Node {
            private String address;
            private String password;
            private int database = 0;
            private int timeout = 3000;
    
            public String getAddress() {
                return address;
            }
    
            public void setAddress(String address) {
                this.address = address;
            }
    
            public String getPassword() {
                return password;
            }
    
            public void setPassword(String password) {
                this.password = password;
            }
    
            public int getDatabase() {
                return database;
            }
    
            public void setDatabase(int database) {
                this.database = database;
            }
    
            public int getTimeout() {
                return timeout;
            }
    
            public void setTimeout(int timeout) {
                this.timeout = timeout;
            }
        }
    }
  4. 配置类:生成多个独立 RedissonClient

    java 复制代码
    @Configuration
    @EnableConfigurationProperties(MultiRedissonProperties.class)
    public class MultiRedissonConfig {
        
        private final Map<String, RedissonClient> clientHolder = new LinkedHashMap<>();
    
        @Bean
        public Map<String, RedissonClient> redissonClientMap(MultiRedissonProperties props) {
            if (props.getNodes() == null || props.getNodes().isEmpty()) {
                throw new IllegalArgumentException("app.redisson.nodes is empty");
            }
            for (Map.Entry<String, MultiRedissonProperties.Node> e : props.getNodes().entrySet()) {
                String name = e.getKey();
                MultiRedissonProperties.Node n = e.getValue();
                Config config = new Config();
                config.useSingleServer().setAddress(n.getAddress()).setDatabase(n.getDatabase()).setTimeout(n.getTimeout());
                if (n.getPassword() != null && !n.getPassword().isBlank()) {
                    config.useSingleServer().setPassword(n.getPassword());
                }
                RedissonClient client = Redisson.create(config);
                clientHolder.put(name, client);
            }
            return clientHolder;
        }
    
        @PreDestroy
        public void shutdownAll() {
            for (RedissonClient c : clientHolder.values()) {
                try {
                    c.shutdown();
                } catch (Exception ignored) {
                }
            }
        }
    }
  5. 业务类中使用(RedLock)

    java 复制代码
    @Service
    public class RedLockService {
        private final RedissonClient c1;
        private final RedissonClient c2;
        private final RedissonClient c3;
    
        public RedLockService(Map<String, RedissonClient> redissonClientMap) {
            this.c1 = required(redissonClientMap, "n1");
            this.c2 = required(redissonClientMap, "n2");
            this.c3 = required(redissonClientMap, "n3");
        }
    
        /**
         * RedLock:多数派成功即可
         */
        public boolean doWithRedLock(String businessKey, Runnable task) {
            RLock l1 = c1.getLock("lock:" + businessKey);
            RLock l2 = c2.getLock("lock:" + businessKey);
            RLock l3 = c3.getLock("lock:" + businessKey);
            RedissonRedLock redLock = new RedissonRedLock(l1, l2, l3);
            boolean locked = false;
            try {
                locked = redLock.tryLock(2, 30, TimeUnit.SECONDS);
                if (!locked) return false;
                task.run();
                return true;
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            } finally {
                if (locked) {
                    try {
                        redLock.unlock();
                    } catch (Exception ignored) {
                    }
                }
            }
        }
    
        /**
         * MultiLock:所有子锁都拿到才成功
         */
        public boolean doWithMultiLock(String businessKey, Runnable task) {
            RLock l1 = c1.getLock("lock:" + businessKey);
            RLock l2 = c2.getLock("lock:" + businessKey);
            RLock l3 = c3.getLock("lock:" + businessKey);
            RLock multiLock = new RedissonMultiLock(l1, l2, l3);
            boolean locked = false;
            try {
                locked = multiLock.tryLock(2, 30, TimeUnit.SECONDS);
                if (!locked) return false;
                task.run();
                return true;
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            } finally {
                if (locked) {
                    try {
                        multiLock.unlock();
                    } catch (Exception ignored) {
                    }
                }
            }
        }
    
        private static RedissonClient required(Map<String, RedissonClient> map, String key) {
            RedissonClient c = map.get(key);
            if (c == null) {
                throw new IllegalArgumentException("missing redisson node config: " + key);
            }
            return c;
        }
    }

参考资料

  1. https://www.bilibili.com/video/BV13R4y1v7sP

注意事项

  1. 部分内容由AI生成
  2. 如有不对,欢迎指正!!!
相关推荐
我爱cope2 小时前
【Agent智能体4 | 智能体AI的应用】
数据库·人工智能·职场和发展
知识分享小能手3 小时前
Flask入门学习教程,从入门到精通,数据库操作 — 知识点详解与案例代码(4)
数据库·学习·flask
我是一颗柠檬3 小时前
【MySQL全面教学】MySQL基础SQL语句Day3(2026年)
数据库·后端·sql·mysql·oracle
XS0301063 小时前
MyBatis动态SQL
数据库·sql·mybatis
MandalaO_O4 小时前
MyBatis 与 MySQL 执行流程
数据库·mysql·mybatis
Donk_675 小时前
ELK+Redis架构搭建
redis·elk·架构
l1t5 小时前
DeepSeek总结的将 Rust Delta Kernel 集成到 ClickHouse
数据库·clickhouse·rust
qq_283720055 小时前
万字深度:Chroma 向量数据库全解析 — 核心原理、实战操作、性能优化与工程最佳实践
数据库·性能优化
黄筱筱筱筱筱筱筱5 小时前
二进制包安装MySql服务
数据库
初心未改HD5 小时前
LLM应用开发之向量数据库详解
数据库·人工智能