客户端请求加锁时,向Redis中写入一个键值对,键为锁的名称,值为客户端的唯一标识符,并设置一个过期时间,以防止锁一直被占用而不能释放。
过期时间具体设置多少合适?
客户端上锁后可以使用Timer,每隔一段时间检查是否还持有锁,如果持有则延长锁的时间,避免接口业务处理时间过长导致锁失效问题。
如果释放锁资源不判断值的唯一标识符,会发生什么?
接口业务处理时间大于锁过期时间,第一个客户端因锁过期丢失了锁,此时第二个客户端上锁,当第一个客户端执行完释放锁,却释放了第二个客户端的锁
非原子操作:用lua脚本保证原子性
@RequestMapping("/deduct_stock")
public String deductStock() throws InterruptedException {
String lockKey = "product_001";
String clientId = UUID.randomUUID().toString();
try {
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS);
if (!result) {
return "error";
}
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock");
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("扣减成功,剩余库存:" + realStock + "");
} else {
System.out.println("扣减失败,库存不足");
}
} finally {
if (clientId.equals(stringRedisTemplate.get(lockKey))) {
stringRedisTemplate.delete(lockKey);
}
}
}
Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。
一个基于Redis实现的分布式工具,有基本分布式对象和高级又抽象的分布式服务,为每个试图再造分布式轮子的程序员带来了大部分分布式问题的解决办法。
@RequestMapping("/deduct_stock")
public String deductStock() throws InterruptedException {
String lockKey = "product_001";
String clientId = UUID.randomUUID().toString();
RLock redissonLock = redisson.getLock(lockKey);
try {
redissonLock.lock(30, TimeUnit.SECONDS);
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock");
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("扣减成功,剩余库存:" + realStock + "");
} else {
System.out.println("扣减失败,库存不足");
}
} finally {
redissonLock.unlock();
}
}
主从切换导致锁失效问题如何解决?