SpringBoot + Redisson 实现分布式锁实战(附业务案例)

在实际开发中,经常会遇到多实例同时操作同一份数据的情况,如果没有加锁,很容易造成数据不一致。本文以一个试卷解析业务为例,讲解如何使用 Redisson 分布式锁 保证多实例安全写库。


一、业务背景

在我们的试卷解析系统中,解析结果会先写入 Redis 缓存,然后批量写入数据库。

如果多个节点同时处理同一张卷子,可能会出现以下问题:

  • 数据库被重复覆盖,导致题目解析丢失

  • 错题本写入出现覆盖问题

解决办法:在批量写库前加 分布式锁,保证同一时间只有一个节点处理某张卷子。


二、什么是分布式锁

在单机环境中,我们可以用 synchronizedReentrantLock 来保证线程安全。

在分布式环境中(多台应用同时操作同一份数据),本地锁无法保证安全。

分布式锁的目标:同一份数据,在同一时间只能被一个实例修改


三、为什么选择 Redisson

Redisson 是基于 Redis 的分布式锁实现,优势:

  • 支持可重入锁,类似 Java 的 ReentrantLock

  • 看门狗机制:自动续租,避免任务超时导致锁被提前释放

  • 提供公平锁、读写锁等多种类型

  • 封装简单易用,减少手动实现 SETNX + EXPIRE 的复杂度


四、Redisson 分布式锁基本使用

1. 添加依赖

复制代码
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.23.5</version>
</dependency>

2. 配置 Redis

复制代码

spring: redis: host: 127.0.0.1 port: 6379

3. 简单示例

复制代码
@Autowired
private RedissonClient redissonClient;

public void doSomething() {
    RLock lock = redissonClient.getLock("test:lock");
    try {
        // 最多等待5秒获取锁
        if (lock.tryLock(5, TimeUnit.SECONDS)) {
            System.out.println("加锁成功,执行任务");
            Thread.sleep(10000); // 模拟耗时操作
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
            System.out.println("任务完成,释放锁");
        }
    }
}

五、结合业务场景实战

在试卷解析系统中,我们实现了如下方法,将 Redis 缓存的解析结果刷回数据库:

复制代码
@Transactional(rollbackFor = Exception.class)
public void flushToDatabase(Long correctId) {
    String lockKey = "correct:lock:" + correctId;
    RLock lock = redissonClient.getLock(lockKey);
    boolean locked = false;
    try {
        locked = lock.tryLock(5, TimeUnit.SECONDS);
        if (!locked) {
            System.out.println("任务正在被处理,跳过 correctId=" + correctId);
            return;
        }

        // 读取 Redis 缓存并更新数据库
        Map<Object, Object> cacheMap = RedisUtils.getHashEntries("correct:cache:" + correctId);
        if (cacheMap != null) {
            updateDatabase(correctId, cacheMap);
            RedisUtils.del("correct:cache:" + correctId);
        }

    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    } finally {
        if (locked && lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

亮点

  • 看门狗机制 :即使 updateDatabase 处理时间超过 30 秒,Redisson 会自动续期锁,防止被其他实例抢占

  • 安全释放锁isHeldByCurrentThread() 避免误释放他人锁

  • 原子性 :保证同一 correctId 的刷库操作不会被多个节点同时执行


六、注意事项

  1. tryLock 的参数:

    • waitTime:最多等待多久获取锁

    • leaseTime:锁自动释放时间(不传则用看门狗机制)

  2. 务必在 finally 里释放锁,并判断 isHeldByCurrentThread()

  3. 避免使用 Redis KEYS * 扫描全量缓存,生产环境可用 SCAN 或维护一个 Set 存放待刷的 correctId


七、总结

  • 分布式锁是解决多实例并发问题的利器

  • Redisson 封装了自动续租机制,大幅降低开发复杂度

  • 在刷库、库存扣减、任务幂等等场景下都非常适用

通过本文案例,你可以在自己的 SpringBoot 项目中安全使用 Redisson 分布式锁,实现多实例安全写库。