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. 如有不对,欢迎指正!!!
相关推荐
kisdiem1 天前
RAG ENGINEERING · 中文教程从文档到可靠答案
数据库
SilentSamsara1 天前
向量数据库实战:Chroma/Milvus/Qdrant 选型与语义搜索应用
开发语言·数据库·人工智能·python·青少年编程·milvus
沪漂阿龙1 天前
LangChain 系列之Agent:从固定流程到模型自主决策
服务器·数据库·langchain
zh_xuan1 天前
PC端操作SQLite数据库
数据库·c++·sqlite
MXsoft6181 天前
**采集节点主备模:保障监控系统自身高可用**
数据库
yyuuuzz1 天前
独立站运营的几个技术层面常见问题
大数据·运维·服务器·网络·数据库·aws
IT策士1 天前
Redis 从入门到精通:Redis Stream —— 可靠消息队列
数据库·redis·缓存
北风toto1 天前
深度拆解:本体与智能体协同生成SQL的底层逻辑与工程实践
数据库·sql·microsoft
倒流时光三十年1 天前
PostgreSQL NULLIF 条件表达式函数详解
数据库·sql·postgresql
代码小库1 天前
【2026前端转 AI 全栈指南】第 2 章(下):NestJS 项目创建 · MongoDB 配置 · 项目启动与调试
前端·数据库·mongodb