【📕分布式锁通关指南 10】源码剖析redisson之MultiLock的实现

简介: Redisson 的 MultiLock 是一种分布式锁实现,支持对多个独立的 RLock 同时加锁或解锁。它通过“整锁整放”机制确保所有锁要么全部加锁成功,要么完全回滚,避免状态不一致。适用于跨多个 Redis 实例或节点的场景,如分布式任务调度。其核心逻辑基于遍历加锁列表,失败时自动释放已获取的锁,保证原子性。解锁时亦逐一操作,降低死锁风险。MultiLock 不依赖 Lua 脚本,而是封装多锁协调,满足高一致性需求的业务场景。

引言

本期我们将把目光聚焦在 Redisson 中另一个颇具代表性的分布式锁实现——MultiLock。它的核心思想是:一次性对多个独立的 RLock 进行加锁或解锁操作,只有当多个锁都成功加锁时才算真正完成锁的获取,一旦有任何一个失败,整体操作都会回滚。这种“整锁整放”的方式,能更好地满足某些高要求的分布式业务场景。

介绍

在分布式环境中,如果我们将数据拆分到不同的 Redis 实例、集群或是不同的 key 上,有时会遇到需要“一次性对 N 个资源都上锁,才算占用资源”的场景。使用 Redisson 的 MultiLock 可以极大地简化这类需求的实现。它提供了一个整合多个 RLock 的抽象,对外暴露成单一的锁接口,使用起来就像在操作一把锁,而内部却是对多把锁的组合操作。

它的典型应用场景包括:

  • 同时锁定多个不同 Redis key,保证“要么全加锁成功,要么全部不加锁”。
  • 跨多个机房 / Redis 节点时的互斥需求,尤其在做异步、分布式任务调度时,减少了操作多个锁的繁琐。

接下来让我们直击源码,看一下 RedissonMultiLock(简称 MultiLock)是如何实现这一套逻辑的。

加锁

在 Redisson 源码里,MultiLock 的主要实现类是 org.redisson.RedissonMultiLock。其核心属性是一个 List<RLock>,用来保存所有需要一起加锁的锁。它本质上也是一个 java.util.concurrent.locks.Lock,所以有类似的 lock()tryLock() 等方法。

下面截取关键的加锁逻辑(为方便说明,做了适当精简):

public class RedissonMultiLock implements Lock {
   

    private final List<RLock> locks = new ArrayList<>();

    public RedissonMultiLock(RLock... locks) {
   
        this.locks.addAll(Arrays.asList(locks));
    }

    @Override
    public void lock() {
   
        lock(-1, null);
    }

    @Override
    public void lock(long leaseTime, TimeUnit unit) {
   
        boolean locked = tryLock(leaseTime, unit);
        if (!locked) {
   
            throw new IllegalStateException("Unable to acquire lock");
        }
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
   
        long startTime = System.currentTimeMillis();
        List<RLock> acquiredLocks = new ArrayList<>();
        for (RLock lock : locks) {
   
            // 计算剩余的等待时间
            long elapsed = System.currentTimeMillis() - startTime;
            long remain = time - elapsed;
            if (remain <= 0 && time != -1) {
   
                // 超时了,回滚所有已获取的锁
                unlockInner(acquiredLocks);
                return false;
            }

            // 尝试获取锁
            boolean success;
            if (time == -1) {
   
                success = lock.tryLock(); // 不限等待时间
            } else {
   
                success = lock.tryLock(remain, unit);
            }

            if (!success) {
   
                // 获取锁失败,回滚
                unlockInner(acquiredLocks);
                return false;
            }
            acquiredLocks.add(lock);
        }
        return true;
    }

    private void unlockInner(List<RLock> locks) {
   
        for (RLock lock : locks) {
   
            try {
   
                lock.unlock();
            } catch (Exception e) {
   
                // 异常处理,通常记录日志或忽略
            }
        }
    }

    // ...
}

从这段代码中,我们可以看到:

  1. MultiLock 会将“要一起加锁”的多个 RLock 封装进一个 List
  2. 当调用 tryLock 时,会挨个尝试获取每个 RLock
  3. 若所有锁都获取成功,才返回 true
  4. 如果中途有任何一个锁获取失败或者超时,就会调用 unlockInner 方法,依次释放已成功获取的锁,回滚到初始状态,保持分布式环境下的原子性。

这样就保障了“要么所有锁都成功加锁,要么一个都不会留存”,消除了状态不一致的风险。

释放锁

MultiLock 的释放逻辑同样是“全部释放”或“都不释放”。来看下相关核心方法 unlock() 的实现(截取简化版本):

public void unlock() {
   
    // 这里我们是一次性 unlock 所有 locks
    for (RLock lock : locks) {
   
        try {
   
            lock.unlock();
        } catch (Exception e) {
   
            // 可能出现解锁异常,比如锁不属于当前线程等情况,做必要处理
        }
    }
}

可以看到,unlock() 内部直接遍历所有 RLock 并执行解锁操作。这意味着无论中途某个锁因为“非本线程占用”等原因导致报错,其余锁也会继续解锁,力求释放尽可能多的锁,尽量避免“部分锁未释放”造成死锁风险。

以此也体现了 MultiLock 的一贯思路:要么全部锁住,要么全部释放,让多个分布式锁在逻辑上“捆绑”成一体。

如何保证“一致性”?

  1. 获取失败即回滚
    当调用 tryLock 时,如果期间任何一个分布式锁无法加锁成功,就立即回滚(释放已获取的锁)。这是确保多锁原子性的关键。
  2. 重复可重入语义仍需依赖具体 RLock
    如果多个 RLock 中有些是可重入锁,那么在同一线程下反复获取时,并不会阻塞。MultiLock 并不会额外重写可重入逻辑,它更多地是一个“协调器”,背后依然由各个 RLock 自身的 reentrant 实现来支撑。
  3. 统一的超时控制
    tryLock(long time, TimeUnit unit) 会逐一减少剩余可用时间,避免因为某个锁获取太慢导致整个流程卡死。
  4. 释放过程对每个锁都负责
    哪怕出现解锁异常,MultiLock 也会继续释放其他锁,将风险与影响降至最低。

小结

RedissonMultiLock 通过将多把 RLock 打包成一个“组合锁”,让使用者在编程时只需关心“我拿到所有锁了吗?所有锁都释放了吗?”。它背后通过遍历加锁并回滚的策略,保证了原子性,避免了分布式环境下常见的“锁定不一致”问题。

与之前的公平锁等其他锁实现相比,MultiLock 并不是通过 Lua 脚本在单个 Redis 实例上实现的,而是通过对多个锁对象的封装来保证“一起成功或一起失败”。它更多用于满足“一次性锁定多资源”的场景,这比单一锁更适用分布式业务中对一致性、原子性要求更高的场景。

希望本文能帮助大家厘清 MultiLock 的实现原理。与其余 Redisson 锁一样,阅读源码的过程能让我们更好地理解其在分布式场景下如何保证安全与高效,也能启发我们在设计自定义分布式组件时,如何通过“组合”思维来化繁为简。我们下一期再见!

目录
相关文章
|
2月前
|
NoSQL 调度 Redis
分布式锁—5.Redisson的读写锁
Redisson读写锁(RedissonReadWriteLock)是Redisson提供的一种分布式锁机制,支持读锁和写锁的互斥与并发控制。读锁允许多个线程同时获取,适用于读多写少的场景,而写锁则是独占锁,确保写操作的互斥性。Redisson通过Lua脚本实现锁的获取、释放和重入逻辑,并利用WatchDog机制自动续期锁的过期时间,防止锁因超时被误释放。 读锁的获取逻辑通过Lua脚本实现,支持读读不互斥,即多个线程可以同时获取读锁。写锁的获取逻辑则确保写写互斥和读写互斥,即同一时间只能有一个线程获取写锁,
185 17
|
2月前
|
算法 NoSQL Redis
分布式锁—4.Redisson的联锁和红锁
Redisson的MultiLock和RedLock机制为分布式锁提供了强大的支持。MultiLock允许一次性锁定多个资源,确保在更新这些资源时不会被其他线程干扰。它通过将多个锁合并为一个大锁,统一进行加锁和释放操作。RedissonMultiLock的实现通过遍历所有锁并尝试加锁,若在超时时间内无法获取所有锁,则释放已获取的锁并重试。 RedLock算法则基于多个Redis节点的加锁机制,确保在大多数节点上加锁成功即可。RedissonRedLock通过重载MultiLock的failedLocksLi
117 10
|
2月前
|
NoSQL Java Redis
分布式锁—6.Redisson的同步器组件
Redisson提供了多种分布式同步工具,包括分布式锁、Semaphore和CountDownLatch。分布式锁包括可重入锁、公平锁、联锁、红锁和读写锁,适用于不同的并发控制场景。Semaphore允许多个线程同时获取锁,适用于资源池管理。CountDownLatch则用于线程间的同步,确保一组线程完成操作后再继续执行。Redisson通过Redis实现这些同步机制,提供了高可用性和高性能的分布式同步解决方案。源码剖析部分详细介绍了这些组件的初始化和操作流程,展示了Redisson如何利用Redis命令和
|
4月前
|
数据采集 存储 数据可视化
分布式爬虫框架Scrapy-Redis实战指南
本文介绍如何使用Scrapy-Redis构建分布式爬虫系统,采集携程平台上热门城市的酒店价格与评价信息。通过代理IP、Cookie和User-Agent设置规避反爬策略,实现高效数据抓取。结合价格动态趋势分析,助力酒店业优化市场策略、提升服务质量。技术架构涵盖Scrapy-Redis核心调度、代理中间件及数据解析存储,提供完整的技术路线图与代码示例。
399 0
分布式爬虫框架Scrapy-Redis实战指南
|
2月前
|
数据采集 存储 NoSQL
基于Scrapy-Redis的分布式景点数据爬取与热力图生成
基于Scrapy-Redis的分布式景点数据爬取与热力图生成
219 67
|
22天前
|
缓存 NoSQL 算法
高并发秒杀系统实战(Redis+Lua分布式锁防超卖与库存扣减优化)
秒杀系统面临瞬时高并发、资源竞争和数据一致性挑战。传统方案如数据库锁或应用层锁存在性能瓶颈或分布式问题,而基于Redis的分布式锁与Lua脚本原子操作成为高效解决方案。通过Redis的`SETNX`实现分布式锁,结合Lua脚本完成库存扣减,确保操作原子性并大幅提升性能(QPS从120提升至8,200)。此外,分段库存策略、多级限流及服务降级机制进一步优化系统稳定性。最佳实践包括分层防控、黄金扣减法则与容灾设计,强调根据业务特性灵活组合技术手段以应对高并发场景。
335 7
|
5月前
|
NoSQL Java 中间件
【📕分布式锁通关指南 02】基于Redis实现的分布式锁
本文介绍了从单机锁到分布式锁的演变,重点探讨了使用Redis实现分布式锁的方法。分布式锁用于控制分布式系统中多个实例对共享资源的同步访问,需满足互斥性、可重入性、锁超时防死锁和锁释放正确防误删等特性。文章通过具体示例展示了如何利用Redis的`setnx`命令实现加锁,并分析了简化版分布式锁存在的问题,如锁超时和误删。为了解决这些问题,文中提出了设置锁过期时间和在解锁前验证持有锁的线程身份的优化方案。最后指出,尽管当前设计已解决部分问题,但仍存在进一步优化的空间,将在后续章节继续探讨。
768 131
【📕分布式锁通关指南 02】基于Redis实现的分布式锁
|
2月前
|
NoSQL 算法 安全
redis分布式锁在高并发场景下的方案设计与性能提升
本文探讨了Redis分布式锁在主从架构下失效的问题及其解决方案。首先通过CAP理论分析,Redis遵循AP原则,导致锁可能失效。针对此问题,提出两种解决方案:Zookeeper分布式锁(追求CP一致性)和Redlock算法(基于多个Redis实例提升可靠性)。文章还讨论了可能遇到的“坑”,如加从节点引发超卖问题、建议Redis节点数为奇数以及持久化策略对锁的影响。最后,从性能优化角度出发,介绍了减少锁粒度和分段锁的策略,并结合实际场景(如下单重复提交、支付与取消订单冲突)展示了分布式锁的应用方法。
179 3
|
2月前
|
缓存 监控 NoSQL
Redis设计与实现——分布式Redis
Redis Sentinel 和 Cluster 是 Redis 高可用与分布式架构的核心组件。Sentinel 提供主从故障检测与自动切换,通过主观/客观下线判断及 Raft 算法选举领导者完成故障转移,但存在数据一致性和复杂度问题。Cluster 支持数据分片和水平扩展,基于哈希槽分配数据,具备自动故障转移和节点发现机制,适合大规模高并发场景。复制机制包括全量同步和部分同步,通过复制积压缓冲区优化同步效率,但仍面临延迟和资源消耗挑战。两者各有优劣,需根据业务需求选择合适方案。
|
2月前
|
数据采集 存储 NoSQL
分布式爬虫去重:Python + Redis实现高效URL去重
分布式爬虫去重:Python + Redis实现高效URL去重

热门文章

最新文章