目录
- 分布式锁的应用场景
- 常见的锁实现方案
- Redisson实现分布式锁的最佳实践
- 方案对比与选择建议
分布式锁的应用场景
在分布式系统中,常常需要控制对共享资源的访问。典型的应用场景包括:
- 缓存击穿防护:防止大量请求同时查询数据库
- 库存扣减:确保商品库存操作的原子性
- 防重复提交:避免表单重复提交
- 定时任务互斥:确保定时任务只被一个节点执行
常见的锁实现方案
1. 本地锁 synchronized
public class LocalLockExample {
public synchronized void doSomething() {
// 业务逻辑
}
}
特点:
- 只能在单机环境下使用
- JVM级别的锁
- 无法解决分布式环境下的并发问题
2. 基于Redis的抢占式锁(SET NX + Lua)
public class RedisLockExample {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
/**
* 尝试获取分布式锁
*/
public boolean tryLock(String lockKey, String requestId, int expireTime) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST,
SET_WITH_EXPIRE_TIME, expireTime);
return LOCK_SUCCESS.equals(result);
}
/**
* 释放分布式锁
*/
public boolean releaseLock(String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey),
Collections.singletonList(requestId));
return Long.valueOf(1L).equals(result);
}
}
特点:
- 使用Redis的原子操作实现
- 需要自己处理锁的超时和续期
- 可能存在锁超时被释放的问题
3. Redisson分布式锁
public class RedissonLockExample {
private final RedissonClient redisson;
public void doWithLock() {
RLock lock = redisson.getLock("myLock");
try {
// 尝试加锁,最多等待30秒,锁定后10秒自动解锁
boolean isLocked = lock.tryLock(30, 10, TimeUnit.SECONDS);
if (isLocked) {
// 业务逻辑
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
}
特点:
- 实现了可重入锁
- 支持自动续期(看门狗机制)
- 提供了丰富的锁类型(公平锁、读写锁等)
- 实现了跨平台的分布式锁
Redisson实现分布式锁的最佳实践
双重检验机制 + Redisson锁
public class OptimizedRedissonLock {
private final RedissonClient redisson;
private final RedisTemplate<String, String> redisTemplate;
public Map<String, Object> getDataWithLock() {
// 第一次检查缓存
String cacheData = redisTemplate.opsForValue().get("cacheKey");
if (!StringUtils.isEmpty(cacheData)) {
return JSON.parseObject(cacheData);
}
// 获取分布式锁
RLock lock = redisson.getLock("myLock");
try {
boolean isLocked = lock.tryLock(30, TimeUnit.SECONDS);
if (!isLocked) {
throw new RuntimeException("获取锁失败");
}
// 第二次检查缓存
cacheData = redisTemplate.opsForValue().get("cacheKey");
if (!StringUtils.isEmpty(cacheData)) {
return JSON.parseObject(cacheData);
}
// 查询数据库
Map<String, Object> dbData = queryDatabase();
// 放入缓存
redisTemplate.opsForValue().set("cacheKey",
JSON.toJSONString(dbData), 1, TimeUnit.DAYS);
return dbData;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("获取锁被中断");
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
看门狗机制说明
Redisson的看门狗机制是一个自动延期机制:
-
原理:
- 默认锁的有效期为30秒
- 如果业务未完成,会自动延长锁的有效期
- 每隔10秒(默认值)自动续期
-
配置方式:
Config config = new Config();
config.setLockWatchdogTimeout(30000); // 设置看门狗超时时间,单位:毫秒
方案对比与选择建议
特性 | synchronized | Redis SET NX | Redisson |
---|---|---|---|
实现难度 | 简单 | 中等 | 简单 |
分布式支持 | 不支持 | 支持 | 支持 |
可重入性 | 支持 | 需自行实现 | 支持 |
自动续期 | 不需要 | 需自行实现 | 支持 |
锁超时处理 | 不需要 | 需自行处理 | 自动处理 |
性能 | 高 | 中等 | 中等 |
可靠性 | 高 | 一般 | 高 |
选择建议
-
单机应用:使用synchronized即可
-
简单分布式场景:
- 性能要求不高
- 锁定时间短
- 可以使用Redis SET NX方案
-
复杂分布式场景:
- 需要可重入锁
- 锁定时间不确定
- 建议使用Redisson