文章目录
- redis乐观锁
- 1. watch 监控key
- 2. multi 开启事务
- 3. exec 执行事务
- 4. 演示
- 1) 先用两个连接AB访问redis
- 2) A监控key,此时库存是4501
- 3) A开启事务,并且将库存-1,事务进入队列等待执行
- 4)此时B更新库存为2001
- 5)A开始执行事务
- 业务改造
- 1. StockService
- 测试
- 问题
redis乐观锁
由watch multi exec配合实现乐观锁
1. watch 监控key
可以监控一个或者多个key的值,若在事务执行(exec)之前,key的值发生变化则取消事务执行
2. multi 开启事务
3. exec 执行事务
4. 演示
1) 先用两个连接AB访问redis
2) A监控key,此时库存是4501
3) A开启事务,并且将库存-1,事务进入队列等待执行
4)此时B更新库存为2001
5)A开始执行事务
执行为空,即执行失败,且库存已被B更新为了2001
6)若事务执行过程中没有被B执行呢
执行成功!!!
业务改造
1. StockService
@Service
public class StockService {
@Autowired
private StockMapper stockMapper;
@Autowired
private StringRedisTemplate redisTemplate;
public void deduct() {
redisTemplate.execute(new SessionCallback<Object>() {
@Override
public <K, V> Object execute(RedisOperations<K, V> redisOperations) throws DataAccessException {
// watch 监控key
redisOperations.watch((K) "stock");
// 1。 查询库存
String stockStr = redisTemplate.opsForValue().get("stock");
if (!StringUtil.isNullOrEmpty(stockStr)) {
int stock = Integer.parseInt(stockStr);
// 2。 判断条件是否满足
if (stock > 0) {
// multi 开启事务
redisOperations.multi();
// 3 更新redis
redisTemplate.opsForValue().set("stock", String.valueOf(stock - 1));
// exec 执行事务
List<Object> list = redisOperations.exec();
// 如果执行事务返回结果为空,则重试
if (CollectionUtils.isEmpty(list)) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
deduct();
}
}
}
return null;
}
});
}
}
需要在redisTemplate.execute(new SessionCallback() {})的匿名内部类中实现redis事务。
而RedisOperations
其实就是我们使用的StringRedisTemplate,所以直接用它调用即可。
测试
性能太差了,吞吐量只有6
但库存成功清0了,问题解决
问题
性能太差