Redis分布式锁

7

Redis分布式锁

1. Redis 分布式锁的核心思路

拿到锁的标准步骤:

  1. 加锁SET key value NX PX ttl
    • NX = 不存在时才创建
    • PX = 设置过期时间(防止死锁)
  2. 执行你的业务逻辑
  3. 释放锁
    • 必须保证只能自己释放自己的锁!
    • 需要判断 value 是否是自己设置的,再删除

这就是一个标准、合格的分布式锁。

2. 用 Redisson 简单搞定分布式锁(推荐,生产可用)

引入 Redisson 依赖

使用 RedissonClient

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class RedissonLockService {

    @Autowired
    private RedissonClient redissonClient;

    public void doBusiness() {
        RLock lock = redissonClient.getLock("lock:demo");

        boolean locked = false;
        try {
            // 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
            locked = lock.tryLock(100, 10, TimeUnit.SECONDS);
            if (locked) {
                System.out.println("成功获得锁,处理业务...");
                // 你的业务逻辑
                Thread.sleep(5000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (locked) {
                lock.unlock();
                System.out.println("释放锁");
            }
        }
    }
}

注意事项:

  • tryLock(等待时间, 锁超时时间, 时间单位),防止死锁。
  • Redisson 的锁是可重入锁(同一线程可以多次加锁)。

3. 分布式锁常见坑和注意点

注意点 说明
锁必须有过期时间 防止服务挂了导致锁永远不释放
释放锁要校验是否是自己加的 不能乱删别人的锁(用value判断)
不要一次性持锁时间太长 锁超时后可以自动续租(Redisson帮你搞定)
加锁和释放锁需要保证原子性 用 Lua 脚本搞定
Redis异常时要处理兜底方案 比如锁失败就降级处理

4. Redis分布式锁的高级用法,主要是 Redisson 提供的这些:

类型 说明 典型场景
公平锁 (Fair Lock) 谁先来排队,谁先拿锁,严格顺序 秒杀、限流,防止插队
可重入锁 (Reentrant Lock) 同一个线程可以多次加锁,不会死锁 递归调用、A方法调B方法
读写锁 (ReadWrite Lock) 读读共享,读写互斥 读多写少的数据同步(缓存一致性)
红锁 (Red Lock) 多个Redis实例保证高可用 多机房高可用一致性锁
联锁 (MultiLock) 多把锁同时拿到才算成功 多资源绑定在一起必须统一加锁

4.1 公平锁(RFairLock)

特点: 谁先来谁先加锁,排队公平。

RLock fairLock = redissonClient.getFairLock("lock:fair");

try {
    if (fairLock.tryLock(10, 30, TimeUnit.SECONDS)) {
        System.out.println("获得公平锁,处理业务...");
        // 业务逻辑
    }
} finally {
    fairLock.unlock();
}

✅ 用在哪?

  • 比如秒杀系统,为了防止后来的请求"插队"抢锁。
  • 公平锁性能稍低于普通锁,因为要排队。

4.2 可重入锁(RLock 默认支持)

特点: 一个线程可以多次加锁,释放的时候也要释放多次。

RLock lock = redissonClient.getLock("lock:reentrant");

lock.lock(); // 第一次加锁
lock.lock(); // 第二次加锁

try {
    System.out.println("两次加锁成功,处理业务...");
} finally {
    lock.unlock();
    lock.unlock(); // 需要两次unlock
}

✅ 用在哪?

  • 方法A加了锁,方法B又加了一次锁,不会死锁。
  • 常见于递归调用、内层方法需要继续拿锁。

4.3 读写锁(RReadWriteLock)

特点:

  • 多个读可以并发。
  • 有写就互斥,写期间不能读。
RReadWriteLock rwLock = redissonClient.getReadWriteLock("lock:rw");

// 读锁
RLock readLock = rwLock.readLock();

// 写锁
RLock writeLock = rwLock.writeLock();

// 读操作
readLock.lock();
try {
    System.out.println("获得读锁,读取数据...");
} finally {
    readLock.unlock();
}

// 写操作
writeLock.lock();
try {
    System.out.println("获得写锁,写入数据...");
} finally {
    writeLock.unlock();
}

✅ 用在哪?

  • 缓存读写,比如配置信息,读远多于写。
  • 保证写的时候不会被并发读到旧数据。

4.4 红锁(RedLock)

特点:

  • 多个Redis节点上同时加锁。
  • 半数以上节点成功,就算加锁成功。
RLock lock1 = redissonClient.getLock("lock:red1");
RLock lock2 = redissonClient.getLock("lock:red2");
RLock lock3 = redissonClient.getLock("lock:red3");

RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);

try {
    redLock.lock(10, TimeUnit.SECONDS);
    System.out.println("成功加了红锁,开始执行任务");
} finally {
    redLock.unlock();
}

✅ 用在哪?

  • 多数据中心部署的系统。
  • 单机Redis不可用时,也能保证锁的有效性。

4.5 联锁(MultiLock)

特点:

  • 必须所有锁都拿到才算成功。
  • 适合多资源必须一起锁定的场景。
RLock lockA = redissonClient.getLock("lock:a");
RLock lockB = redissonClient.getLock("lock:b");
RLock lockC = redissonClient.getLock("lock:c");

RedissonMultiLock multiLock = new RedissonMultiLock(lockA, lockB, lockC);

try {
    multiLock.lock(10, TimeUnit.SECONDS);
    System.out.println("成功拿到多把锁,开始处理业务");
} finally {
    multiLock.unlock();
}

✅ 用在哪?

  • 比如订单服务锁住订单表、库存表、物流表一起处理。

✍️ 小结一下

类型 特点 适用场景
公平锁 严格排队顺序加锁 秒杀、限流防止插队
可重入锁 同线程多次加锁不会死锁 嵌套调用
读写锁 读并发,写独占 配置读取、缓存读写
红锁 多节点加锁,高可用 分布式多机房系统
联锁 多资源必须同时加锁 复杂业务跨表操作