redis实现分布式锁踩坑记录

本文分享了在高并发场景下使用Redis实现分布式锁时遇到的挑战与解决方案,包括数据库事务超时、key过期、连接池爆满等问题,以及如何正确处理锁的释放,确保线程安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里主要记录项目中使用基于redis的分布式锁所遇到的问题及解决方案;

业务场景

我的业务场景是这样的,我们服务有库存模块,而我的服务又是多节点部署,要高峰期会存在库存差异,后面分析问题之后,打算采用redis实现分布式锁(主要的原因是服务已经集成了redis,不需要做额外的配置)

踩坑1. 数据库事务超时

不要感觉奇怪,分布式锁怎么会导致数据库事务超时呢?
我的代码大概是这样的:

伪代码
@Transaction(readOnly=false)
void update(){
    do{
        redis=JedisUtil.getJedis();
        flag = getLock(key,redis);
        if(flag){
            update();
        }
    }while(true)
}

当你的key长时间获取不到锁,并且数据库事务都有超时时间的限制,那么就会出现数据库事务超时问题; 

解决方案

数据库事务改为手动提交事务;

踩坑2. redis key过期,而业务没有执行完

我的key的过期时间设置的是30s,如果30秒业务还没有执行完毕,锁就会自动释放,锁释放之后,其它线程又会去占用锁,同样会导致问题的发生;
解决方案

最简单的解决方案就是使用redisson;
如果非要用redis来解决的话,只能使用定时器去检测key,如果说key还有2秒就快过期了,那么再为key重新设置30秒的过期时间;

踩坑3. redis连接池爆满

分布式锁刚加上之后,生产出现一个问题,就是:redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
解决办法
开始查代码,发现是开发人员没有对连接进行释放;

修复bug之后,又在线上跑了一段时间,又出现了redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
解决办法

void update(){
    do{
        redis=JedisUtil.getJedis();
        flag = getLock(key,redis);
        if(flag){
            update();
        }else{
            // 释放当前redis连接
            // 由于我们的业务场景属于比较耗时的业务型,所以在这里休眠1000毫秒
            redis.close();
            sleep(1000);
        }
    }while(true)
}

1.当前请求获取锁,如果获取不到,则释放当前连接,并休眠一会;
2.合理配置redis连接池大小,主要参考具体业务场景的并发量来设置;

踩坑4. 解铃还须系铃人

回顾一下加锁的参数:

set(key, vlue,"NX","PX", 30000);

其中:value,我使用它来表示加锁人,必须是一个唯一的标识

比如:
A线程 key=test value=01
B线程 key=test value=02

如果A线程执行业务耗时超过了锁的持有时间,锁会自动释放;锁自动释放之后,线程B又加锁成功,但是,此时A线程执行完业务逻辑之后,去释放锁,但A线程的锁已经自动释放了,如果没有value来标识的话,它可能就会去释放B线程的锁;

踩坑5. redis集群实现分布式锁

这种情况我没有遇到,因为公司的redis集群做了改进;

先说一下这种问题产生的原因:
如果master节点由于某原因发生了主从切换,那么就会出现锁丢失的情况;

  • 在master节点上拿到了锁;
  • 但是这个加锁的key还没有同步到slave节点;
  • master故障,发生故障转移,slave节点升级为master节点;
  • 故导致锁丢失;

解决办法

需要通过使用redlock算法;
或使用redisson,它有对redlock算法做封装;

<think>好的,我现在需要向用户介绍Redisson。用户之前询问了如何实现Redis分布式锁,现在想了解Redisson,所以可能是想找一个更成熟的解决方案,而不是自己手动实现。 首先,我应该确定用户对Redisson的了解程度。用户可能已经知道Redis分布式锁的基础知识,现在想了解Redisson如何简化这一过程。需要解释Redisson是什么,它的核心功能,以及它如何解决分布式锁中的常见问题。 接下来,需要涵盖Redisson的主要特点。比如,它基于Netty,支持多种数据结构,还有分布式对象和服务。重点要放在分布式锁实现上,包括自动续期、多种锁类型、等待机制等。要对比之前手动实现的方法,突出Redisson的优势,比如自动处理原子性、续期和避免死锁。 还要提到Redisson的高级功能,比如联锁、红锁、读写锁,这些是用户可能需要的复杂场景下的解决方案。另外,集成和配置部分也需要简要说明,让用户知道如何开始使用,比如依赖配置和Spring Boot的集成。 注意事项部分,要提醒用户使用最新版本、合理配置超时时间,以及集群模式的注意事项。这些是实际应用中容易遇到的问题,提前说明可以帮助用户避免。 最后,强调推荐使用Redisson的原因,比如社区活跃、文档完善,适合生产环境。可能用户需要的是稳定可靠的解决方案,而Redisson正好符合这些需求。 需要确保语言简洁明了,结构清晰,分点说明,方便用户快速理解。避免使用过多技术术语,但必要的术语需要解释,比如看门狗机制、红锁算法等。同时,结合用户之前的提问,突出Redisson在分布式锁方面的优势,满足用户的实际需求。</think>Redisson 是一个基于 Redis 实现的 Java 驻内存数据网格(In-Memory Data Grid),提供了分布式和可扩展的 Java 对象与服务。它特别擅长处理分布式场景下的并发问题,是 Redis 官方推荐的 Java 客户端之一。 --- ### **核心功能亮点** 1. **分布式锁实现** - **可重入锁**:支持同一线程多次加锁 ```java RLock lock = redisson.getLock("myLock"); lock.lock(); // 默认30秒自动续期 try { // 业务逻辑 } finally { lock.unlock(); } ``` - **看门狗机制**:后台线程自动续期(默认每10秒续期到30秒) - **多种锁类型**: - 公平锁(Fair Lock) - 联锁(MultiLock):同时锁定多个资源 - 红锁(RedLock):多Redis节点分布式锁 - 读写锁(ReadWriteLock) 2. **分布式数据结构** - 分布式 List、Set、Map、Queue - 原子 Long/Double 对象 - 布隆过滤器、基数估计算法 3. **分布式服务** - 分布式远程服务(RPC) - 分布式调度服务(Scheduler) - 分布式发布订阅 --- ### **分布式锁优势对比** | 特性 | 手动实现 Redis 锁 | Redisson 实现 | |--------------------|--------------------------|--------------------------| | 自动续期 | 需自行实现续期逻辑 | 内置看门狗自动续期 | | 可重入性 | 需额外记录重入次数 | 原生支持 | | 锁类型丰富度 | 单一实现 | 支持公平锁、联锁等多种类型 | | 异常处理 | 需自行处理网络异常 | 内置异常恢复机制 | | 集群支持 | 需手动实现 RedLock | 内置 RedLock 算法 | --- ### **关键机制解析** 1. **看门狗续期机制** - 加锁时不指定 `leaseTime` 参数时自动启用 - 后台守护线程每隔(锁过期时间/3)执行续期(默认10秒) - 业务执行时间超过30秒仍能保持锁有效 2. **RedLock 红锁算法** ```java RLock lock1 = redisson1.getLock("lock"); RLock lock2 = redisson2.getLock("lock"); RLock lock3 = redisson3.getLock("lock"); RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3); lock.lock(); ``` - 需要半数以上节点加锁成功 - 自动计算有效时间:锁超时时间 - 获取锁耗时 3. **锁释放保障** - 通过 `try-finally` 确保锁释放 - 强制释放校验: ```java if (lock.isHeldByCurrentThread()) { lock.unlock(); } ``` --- ### **生产实践建议** 1. **推荐配置** ```properties # 连接配置 singleServerConfig: address: "redis://127.0.0.1:6379" connectionPoolSize: 64 idleConnectionTimeout: 10000 # 看门狗配置 lockWatchdogTimeout: 30000 # 默认30秒 ``` 2. **性能指标** - 单节点吞吐量:12万+ ops/sec - 平均延迟:< 1ms(本地Redis) - 集群模式下线性扩展 3. **异常处理方案** - 网络抖动:内置重试机制(默认3次重试) - 节点宕机:红锁模式自动切换存活节点 - 长时间阻塞:使用 `tryLock` 设置等待时间 ```java if (lock.tryLock(5, 30, TimeUnit.SECONDS)) { // 业务逻辑 } ``` --- ### **典型使用场景** 1. **秒杀库存扣减** ```java RLock lock = redisson.getLock("product_1001"); lock.lock(); try { int stock = redis.get("stock"); if (stock > 0) { redis.decr("stock"); } } finally { lock.unlock(); } ``` 2. **全局配置更新** ```java RReadWriteLock rwLock = redisson.getReadWriteLock("configLock"); // 写锁 rwLock.writeLock().lock(); try { updateGlobalConfig(); } finally { rwLock.writeLock().unlock(); } ``` --- ### **注意事项** 1. **版本选择** - JDK 8+ 推荐使用 3.17.x+ 版本 - 与Spring Boot集成时注意版本兼容性 2. **死锁预防** - 避免在锁内执行远程调用等耗时操作 - 设置合理的锁超时时间(不宜过短) 3. **集群模式** - Redis Cluster 需要所有节点存活 - 主从架构建议使用红锁保证可靠性 Redisson 通过 160+ 种分布式对象和服务,将 Redis 扩展为分布式系统的基础设施,是构建高并发分布式系统的利器。其分布式锁实现经过 Amazon DynamoDB 等大型系统验证,适合生产环境直接使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值