使用Redisson实现同一业务类型串行执行的分布式锁方案,解决并发问题

你要的是 “同一业务类型(bussType)串行执行” 的效果 —— 即同一个 bussType 只能被一个线程处理,上一个线程执行完(释放锁),下一个线程才能进入。这种场景用 Redisson 的分布式锁 最合适,它支持细粒度锁(按 bussType 分锁),还能避免死锁、自动续期,比原生 Redis 锁更稳定。
以下是 基于 Redisson 的 “单 bussType 串行执行” 完整实现,直接替换原代码即可,兼顾并发安全和易用性:
核心思路
锁粒度:按 bussType 生成独立锁(比如 code:lock:order、code:lock:user),不同业务类型互不影响(并行执行),同一业务类型串行执行。
锁特性:用 Redisson 的 RLock,支持自动重试、超时释放、自动续期(避免长事务导致锁过期)。
执行逻辑:线程先拿对应 bussType 的锁 → 拿到锁后执行 “查 - 算 - 更” → 执行完释放锁 → 下一个线程竞争锁。
步骤 1:引入 Redisson 依赖(以 Spring Boot 为例)
xml
<!-- Redisson 核心依赖 -->

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.23.3</version> <!-- 推荐用稳定版,和 Spring Boot 版本兼容 -->
</dependency>


步骤 2:配置 Redisson(application.yml)

yaml
spring:
  redis:
    host: 127.0.0.1 # 你的 Redis 地址
    port: 6379
    password: #  Redis 密码(无则留空)
    database: 0

redisson:
  singleServerConfig:
    address: redis://${spring.redis.host}:${spring.redis.port}
    password: ${spring.redis.password}
    database: ${spring.redis.database}
    connectionMinimumIdleSize: 10 # 最小空闲连接数
    connectionPoolSize: 64 # 连接池大小
    idleConnectionTimeout: 10000 # 空闲连接超时时间
    connectTimeout: 10000 # 连接超时时间
    timeout: 3000 # 命令执行超时时间


步骤 3:改造 Service 方法(核心代码)

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

@Service
public class SysCodeServiceImpl extends ServiceImpl<SysCodeConfMapper, SysCodeConfEntity> implements SysCodeService {

    @Autowired
    private RedissonClient redissonClient;

    // 锁前缀:避免和其他 Redis 锁冲突
    private static final String LOCK_PREFIX = "sys_code:lock:";

    // 锁等待时间:5秒(线程最多等5秒拿锁,超时返回失败)
    private static final long LOCK_WAIT_TIME = 5;

    // 锁自动释放时间:30秒(防止线程异常导致锁不释放)
    private static final long LOCK_LEASE_TIME = 30;

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW) // 独立事务,减少锁持有时间
    public String getCodeByType(String bussType) {
        // 1. 生成当前业务类型的专属锁(细粒度锁,不影响其他 bussType)
        RLock lock = redissonClient.getLock(LOCK_PREFIX + bussType);

        try {
            // 2. 尝试获取锁:最多等5秒,拿到锁后30秒自动释放(支持自动续期)
            boolean isLocked = lock.tryLock(LOCK_WAIT_TIME, LOCK_LEASE_TIME, TimeUnit.SECONDS);
            if (!isLocked) {
                log.error("获取编码锁失败,业务类型:{}(当前并发过高,请稍后重试)", bussType);
                throw new RuntimeException("编码生成繁忙,请稍后重试");
            }

            // 3. 拿到锁后,执行核心逻辑(此时同一 bussType 只有当前线程在执行)
            SysCodeConfEntity sysCodeConfEntity = baseMapper.selectOne(
                Wrappers.lambdaQuery(SysCodeConfEntity.class).eq(SysCodeConfEntity::getBussType, bussType)
            );
            if (sysCodeConfEntity == null) {
                log.warn("业务类型{}未配置编码规则", bussType);
                return null;
            }

            // 4. 生成新编码 + 更新数据库(串行执行,无并发冲突)
            String newCode = CodeUtil.generateNewCode(sysCodeConfEntity);
            baseMapper.updateById(sysCodeConfEntity);

            return newCode;

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // 恢复线程中断状态
            log.error("编码生成被中断,业务类型:{}", bussType, e);
            return null;
        } finally {
            // 5. 释放锁(必须在 finally 中执行,确保锁一定释放)
            if (lock.isHeldByCurrentThread()) { // 防止未拿到锁却尝试释放
                lock.unlock();
            }
        }
    }
}


关键特性说明(为什么用 Redisson 而不是原生 Redis 锁?)
自动续期:如果事务执行时间超过 LOCK_LEASE_TIME(30 秒),Redisson 会自动给锁续期(默认每 10 秒续一次),避免锁过期导致并发问题。
非阻塞获取锁:tryLock 方法会在指定时间内尝试获取锁,超时返回失败,不会导致线程一直阻塞。
细粒度锁:每个 bussType 对应一个独立的锁,比如 code:lock:order 和 code:lock:user 是两个不同的锁,不同业务类型可以并行执行,不会全局串行(提升性能)。
死锁防护:锁有超时时间,即使线程异常崩溃,Redis 也会在超时后自动释放锁,避免死锁。

<think>好的,用户想了解如何使用Redisson分布式锁实现并发环境下的库存管理系统,确保多库并发控制的正确性和效率。我需要结合他们提供的引用内容来构建一个全面的回答。 首先,根据引用[1]和[2],Redisson分布式锁的推荐方案,因为它解决了数据库和缓存不一致的问题,并且符合CAP原理。用户可能关心Redisson的基本原理和如何集成到库存系统中。我需要解释Redisson的锁机制,比如可重入锁、锁续期等。 然后,库存管理通常涉及扣减库存的操作,必须保证原子性。这里需要提到使用Redis的原子操作,比如Lua脚本,或者结合Redisson的RLock。引用[3]提到了锁等待时间,这可能和锁的超时设置有关,需要说明如何合理配置锁的过期时间,避免死锁和长时间阻塞。 接下来是多库并发控制的问题。库存系统可能有多个仓库或数据库,分布式锁需要跨所有节点协调。根据引用[1]中的RedLock算法,可能需要多个独立的Redis实例来实现更可靠的锁。不过Redisson已经支持RedLock,可以介绍如何配置和使用。 另外,引用[4]提到锁的粒度问题使用userid作为锁的key,这里库存管理可能需要以商品ID或库存ID作为锁的键,确保同一商品的库存操作串行化,避免超卖。需要强调锁的粒度要合适,太粗会影响并发性能,太细会增加复杂度。 还需要考虑高并发下的性能优化,比如分段锁。引用[3]提到了Go的分段加锁Demo,可能可以借鉴到Java中,将库存分成多个段,每个段单独加锁,提高并发度。例如,将库存拆分为多个子库存,减少锁竞争。 此外,异常处理也很重要,比如锁的自动续期、避免锁泄漏。Redisson的看门狗机制可以自动延长锁的持有时间,防止业务未完成时锁过期。需要提醒用户正确处理锁的获取和释放,确保在异常情况下也能释放锁,比如在finally块中释放。 最后,分布式事务和幂等性可能也是需要考虑的,引用[2]提到了接口的幂等性,库存操作需要保证即使重复请求也不会多次扣减。可以通过唯一事务ID或版本号机制来实现。 总结回答结构:先介绍Redisson分布式锁的核心机制,然后详细说明库存扣减流程,包括锁的获取、原子操作、多库处理,接着讨论锁的优化如分段锁和RedLock,最后强调异常处理和监控。这样用户能一步步了解如何正确高效地实现分布式库存管理。</think>### Redisson分布式锁在库存管理系统中的实现实践 #### 一、核心机制设计 1. **可重入锁机制** Redisson的`RLock`支持线程级别的重入特性,同一线程可重复获取锁,防止死锁。适用于需要嵌套调用的库存操作场景: ```java RLock lock = redissonClient.getLock("product_stock:1001"); lock.lock(10, TimeUnit.SECONDS); // 设置自动过期时间[^1] ``` 2. **看门狗自动续期** 当未显式设置`leaseTime`参数时,Redisson会启动后台线程每10秒检测锁状态并自动续期(默认30秒有效期),避免业务未完成时锁过期[^1]。 #### 二、库存扣减流程实现 ```java public boolean deductStock(String productId, int quantity) { String lockKey = "stock_lock:" + productId; RLock lock = redissonClient.getLock(lockKey); try { // 尝试获取锁(等待5秒,锁持有30秒) if (lock.tryLock(5, 30, TimeUnit.SECONDS)) { int currentStock = redisTemplate.opsForValue().get(productId); if (currentStock >= quantity) { redisTemplate.opsForValue().decrement(productId, quantity); return true; } return false; } } finally { lock.unlock(); } } ``` #### 三、多库并发控制方案 1. **库存分片策略** 采用分段锁提升并发能力,将总库存拆分为N个分片: ```java int shard = productId.hashCode() % 16; String lockKey = "stock_shard_lock:" + shard; ``` 2. **跨库事务协调** 使用RedLock算法实现跨多Redis节点的强一致性: ```java RLock lock1 = redissonClient1.getLock("lockKey"); RLock lock2 = redissonClient2.getLock("lockKey"); RedissonRedLock redLock = new RedissonRedLock(lock1, lock2); ``` #### 四、性能优化实践 | 优化维度 | 实施方法 | 效果提升 | |----------------|-----------------------------------|---------------------------| | 锁粒度控制 | 按商品ID+仓库ID组合锁定 | 降低锁竞争概率 | | 热点库存拆分 | 将1000件库存拆分为10个100件的分片 | 并发能力提升8-10倍 | | 预扣库存策略 | 使用Redis原子操作提前扣减虚拟库存 | 减少数据库压力 | #### 五、异常处理关键点 1. **网络分区防护** 设置合理的`lockWatchdogTimeout`(默认30秒),配合`innodb_lock_wait_timeout`(数据库行锁超时)形成双层保护[^3] 2. **幂等性保证** 为每个操作生成唯一事务ID: ```sql UPDATE inventory SET stock=stock-? WHERE product_id=? AND version=? ``` #### 六、监控指标建议 1. 锁等待时间百分位(P99 ≤ 50ms) 2. 库存操作TPS与锁获取成功率关联分析 3. 分片锁的竞争热度分布
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值