Redis分布式锁
Redis分布式锁
1. Redis 分布式锁的核心思路
拿到锁的标准步骤:
- 加锁(
SET key value NX PX ttl
)NX
= 不存在时才创建PX
= 设置过期时间(防止死锁)
- 执行你的业务逻辑
- 释放锁
- 必须保证只能自己释放自己的锁!
- 需要判断 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();
}
✅ 用在哪?
- 比如订单服务锁住订单表、库存表、物流表一起处理。
✍️ 小结一下
类型 | 特点 | 适用场景 |
---|---|---|
公平锁 | 严格排队顺序加锁 | 秒杀、限流防止插队 |
可重入锁 | 同线程多次加锁不会死锁 | 嵌套调用 |
读写锁 | 读并发,写独占 | 配置读取、缓存读写 |
红锁 | 多节点加锁,高可用 | 分布式多机房系统 |
联锁 | 多资源必须同时加锁 | 复杂业务跨表操作 |