目录
一、分布式锁一般有三种实现方式:
1. 数据库乐观锁;
2. 基于Redis的分布式锁;
3. 基于ZooKeeper的分布式锁。
为了确保分布式锁可用,至少要确保同时满足以下四个条件:
1、互斥性。在任意时刻,只有一个客户端能持有锁;
2、不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁;
3、具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁;
4、解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
二、数据库乐观锁(待补充)
https://blog.youkuaiyun.com/tianjiabin123/article/details/72625156/
三、基于Redis的分布式锁
public class RedisLock {
@Autowired
public StringRedisTemplate stringRedisTemplate;
private static final Logger log = LoggerFactory.getLogger(UserResource.class);
/**
* 加锁
* @param key
* @param value 当前时间+超时时间
* @return
*/
public boolean lock(String key, String value) {
if (stringRedisTemplate.opsForValue().setIfAbsent(key, value)) {
return true;
}
String currentValue = stringRedisTemplate.opsForValue().get(key);
//如果锁过期
if (StringUtils.isEmpty(currentValue) && (Long.parseLong(currentValue) < System.currentTimeMillis())) {
String oldValue = stringRedisTemplate.opsForValue().getAndSet(key, value);
if (!StringUtils.isEmpty(oldValue) && oldValue.equals(value)) {
return true;
}
}
return false;
}
/**
* 解锁
* @param key
* @param value
*/
public void unLock(String key, String value) {
try {
String currentValue = stringRedisTemplate.opsForValue().get(key);
if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
stringRedisTemplate.opsForValue().getOperations().delete(key);
}
} catch (Throwable e) {
log.error("redis分布式锁,解锁异常, {}", e.getMessage(), e);
}
}
}
调用加锁和解锁
public class ProjectService {
private static final int TIMEOUT = 10 * 1000; //超时时间10秒
@Autowired
private RedisLock redisLock;
/**
* 秒杀调用接口
* @param productId
*/
public void orderProductMockDiffUser(String productId) {
long time = System.currentTimeMillis() + TIMEOUT;
if(!redisLock.lock(productId, String.valueOf(time))) {
throw new SellException(10001, "人太多,请重新~");
};
redisLock.unLock(productId, String.valueOf(time));
}
}
使用Redis分布式锁要先熟悉两个命令:
--- 将key设置值为value,如果key不存在,则等同于SET命令。 当key存在时,什么也不做。SETNX是”SET if Not eXists”的简写
SETNX key value
--- 自动将key对应到value并且返回原来key对应的value。如果key存在但是对应的value不是字符串,就返回错误。
GETSET key value