Redis分布式锁
2020-07-14 本文已影响0人
zouhao1985
1.Redis分布式锁概述
除了Redis,还能使用什么作为分布式锁?
利用Redis的setnx(SET if Not eXists)命令来实现分布式锁,由于Redis是单线程单进程,多线程同时对同一个key进行setnx的时候,只有一个能成功(集群也是一样,同一个key通过CRC函数计算,最终key会落到同一个集群的节点)。
redis> SETNX mykey "Hello"
(integer) 1
redis> SETNX mykey "World"
(integer) 0
redis> GET mykey
"Hello"
redis>
2.基于Spring的RedisTemplate配置
- SpringBoot的yaml配置
spring:
redis:
host: 127.0.0.1
port: 6379
timeout: 20000
pool:
max-active: 8
min-idle: 0
max-idle: 8
max-wait: -1
password:
- 代码片段
@RestController
public class DeductStockController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@PostConstruct
public void init() {
stringRedisTemplate.opsForValue().set("stock", String.valueOf(50));
}
@RequestMapping("/deductStock")
public String deductStock() {
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("库存不足");
}
return "end";
}
}
3.分布式锁的三种使用
- 异常使用
@RestController
public class DeductStockV2Controller {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@PostConstruct
public void init() {
stringRedisTemplate.opsForValue().set("stock", String.valueOf(50));
}
@RequestMapping("/deductStockV2")
public String deductStock() {
String lockKey = "lockKey";
Boolean stockLock = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "1");
if (!stockLock) {
System.out.println("没有获取到锁");
return "lock 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("库存不足");
}
stringRedisTemplate.delete(lockKey);
return "end";
}
}
哪里有问题?
- 普通使用
@RestController
public class DeductStockV3Controller {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@PostConstruct
public void init() {
stringRedisTemplate.opsForValue().set("stock", String.valueOf(50));
}
@RequestMapping("/deductStockV3")
public String deductStock() {
String lockKey = "lockKey";
String clientId = UUID.randomUUID().toString();
Boolean stockLock = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS);
// stringRedisTemplate.expire(lockKey, 10, TimeUnit.SECONDS);
if (!stockLock) {
System.out.println("没有获取到锁");
return "lock error";
}
try {
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.opsForValue().get(lockKey))) {
stringRedisTemplate.delete(lockKey);
}
}
return "end";
}
}
- 锁使用完之后应该在finally代码块中释放
- 释放锁的时候需要判断当前锁的对象和释放锁的对象是否一致,一致才能释放
- 还有什么问题?
- 高级使用
Redisson是Java的Redis操作库,功能强大。
@SpringBootApplication
public class RedisApplication {
public static void main(String[] args) {
SpringApplication.run(RedisApplication.class, args);
}
@Bean
public Redisson redisson() {
Config config = new Config();
config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(0);
// config.useClusterServers()
// .addNodeAddress("redis://ip:port")
// .addNodeAddress("redis://ip:port")
// .addNodeAddress("redis://ip:port");
return (Redisson) Redisson.create(config);
}
}
@RestController
public class DeductStockV4Controller {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private Redisson redisson;
@PostConstruct
public void init() {
stringRedisTemplate.opsForValue().set("stock", String.valueOf(50));
}
@RequestMapping("/deductStockV4")
public String deductStock() {
String lockKey = "lockKey";
RLock redissonLock = redisson.getLock(lockKey);
// if (!stockLock) {
// System.out.println("没有获取到锁");
// return "lock error";
// }
try {
redissonLock.lock();
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();
}
return "end";
}
}
使用Redisson帮助我们解决了以上问题。
lock的时候默认30秒时间,同时每隔10秒自动延长锁的时间30秒(守护线程)。