Spring Boot 3.x集成Redis实现分布式锁:Redisson方案与常见坑点排查

微服务架构中,分布式锁用于保证跨服务实例的互斥操作。基于Redis的分布式锁因其高性能和易部署特性被广泛使用。Spring Boot 3.x配合Redisson客户端,在API友好度和可靠性上都有成熟方案。本文实现一个完整的分布式锁组件,覆盖加锁、解锁、自动续期、异常处理全流程。

Redisson与Jedis实现差异

Jedis实现分布式锁通常用SET key value NX PX命令,需要自己处理续期、重入、释放逻辑。Redisson封装了看门狗(Watchdog)自动续期机制,支持可重入锁、公平锁、读写锁、联锁(MultiLock)多种锁类型。在高并发设计场景下,Redisson的API抽象更贴合业务开发需求。

Spring Boot 3.x集成Redisson

添加Redisson Spring Boot Starter依赖。注意Spring Boot 3.x需要Redisson 3.20.0以上版本以兼容Jakarta EE:

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.27.0</version>
</dependency>

application.yml配置:

spring:
  data:
    redis:
      host: 127.0.0.1
      port: 6379
      password: yourpassword
      database: 0

redisson:
  config: |
    singleServerConfig:
      address: "redis://127.0.0.1:6379"
      password: "yourpassword"
      database: 0
      connectionPoolSize: 32
      connectionMinimumIdleSize: 8

分布式锁服务封装

封装一个通用的分布式锁工具类,支持超时设置和自动续期:

@Service
public class DistributedLockService {

    private final RedissonClient redissonClient;

    public DistributedLockService(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    /**
     * 加锁执行
     * @param lockKey 锁key
     * @param leaseTime 持锁时间(秒),-1表示启用看门狗自动续期
     * @param supplier 业务逻辑
     */
    public <T> T executeWithLock(String lockKey, long leaseTime, Supplier<T> supplier) {
        RLock lock = redissonClient.getLock(lockKey);
        boolean acquired = false;
        try {
            acquired = lock.tryLock(0, leaseTime, TimeUnit.SECONDS);
            if (!acquired) {
                throw new RuntimeException("获取锁失败: " + lockKey);
            }
            return supplier.get();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("线程中断", e);
        } finally {
            if (acquired && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}

tryLock的第一个参数是等待时间,0表示不等待。leaseTime设为-1时启用看门狗,默认每10秒续期一次,续期到30秒。如果业务执行时间不确定,用-1最安全。如果业务有明确最大执行时间,设置具体leaseTime避免无限续期。

可重入锁与业务场景实现

Redisson的RLock是可重入锁,同一线程可以多次获取同一把锁。适用场景示例——库存扣减:

@Service
public class InventoryService {

    private final DistributedLockService lockService;
    private final InventoryMapper inventoryMapper;

    public boolean deductStock(String skuCode, int quantity) {
        String lockKey = "inventory:lock:" + skuCode;
        return lockService.executeWithLock(lockKey, 30, () -> {
            Inventory inventory = inventoryMapper.selectBySkuCode(skuCode);
            if (inventory.getStock() < quantity) {
                return false;
            }
            inventory.setStock(inventory.getStock() - quantity);
            inventoryMapper.updateById(inventory);
            inventoryMapper.insertDeductLog(skuCode, quantity);
            return true;
        });
    }
}

分布式事务场景下,如果方法A调用方法B,两者都需要同一把锁,可重入特性保证不会死锁。Redisson通过Hash结构记录锁持有者线程ID和重入次数,解锁时递减计数。

常见问题排查与解决方案

坑点1:锁释放失败导致永久锁

finally块中直接调用lock.unlock(),如果线程未持有锁(如leaseTime到期自动释放后),会抛IllegalMonitorStateException。必须先判断isHeldByCurrentThread:

finally {
    if (lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

坑点2:看门狗续期与JVM Stop-The-World

如果JVM发生长时间GC暂停,看门狗线程可能无法执行续期,导致锁超时自动释放。其他线程获取锁后,原线程恢复执行时出现并发问题。解决方案:对关键业务设置明确的leaseTime略大于业务最大执行时间,并捕获unlock时的异常做幂等处理。

坑点3:Redis主从切换导致锁失效

Redis主节点加锁成功后异步同步到从节点。如果主节点宕机,从节点提升为主时尚未收到锁数据,其他客户端可以重复加锁。Redisson提供RedLock算法,向多个独立Redis节点同时加锁,多数成功即认为加锁成功:

RLock lock1 = redissonClient.getLock("lock_key");
RLock lock2 = redissonClient2.getLock("lock_key");
RLock lock3 = redissonClient3.getLock("lock_key");

RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
redLock.lock(30, TimeUnit.SECONDS);
try {
    // 业务逻辑
} finally {
    redLock.unlock();
}

RedLock的争议在于增加了延迟和复杂度。对于金融级强一致性要求场景,建议使用Zookeeper或etcd实现的分布式锁。服务治理中根据业务场景选择合适的一致性级别,而非一味追求Redis方案。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot3x-ji-cheng-redis-shi-xian-fen-bu-shi-suo/

(0)
小编小编
上一篇 15小时前
下一篇 15小时前

相关推荐