分布式锁实现----Redis

实现原理:setnx

实现逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class GrabRedisLockServiceImpl implements GrabService {

@Autowired
StringRedisTemplate stringRedisTemplate;

@Autowired
OrderService orderService;

@Override
public String grabOrder(int orderId , int userId){
//生成key
String lock = "order_"+(orderId+"");
/*
* 情况一,如果锁没执行到释放,比如业务逻辑执行一半,运维重启服务,或 服务器挂了,没走 finally,怎么办?
* 加超时时间
* setnx
*/
// boolean lockStatus = stringRedisTemplate.opsForValue().setIfAbsent(lock.intern(), userId+"");
// if(!lockStatus) {
// return null;
// }

// 情况二:加超时时间,会有加不上的情况,运维重启
// boolean lockStatus = stringRedisTemplate.opsForValue().setIfAbsent(lock.intern(), userId+"");
// stringRedisTemplate.expire(lock.intern(), 30L, TimeUnit.SECONDS);
// if(!lockStatus) {
// return null;
// }

// 情况三:超时时间应该一次加,不应该分2行代码,
boolean lockStatus = stringRedisTemplate.opsForValue().setIfAbsent(lock.intern(), userId+"", 30L, TimeUnit.SECONDS);
// 开个子线程,原来时间N,每个n/3,去续上n
// TODO renewLock();
if(!lockStatus) {
return null;
}
try {
System.out.println("抢单逻辑");
boolean b = orderService.grab(orderId, userId);
if(b) {
System.out.println(userId+" 抢单成功");
}else {
System.out.println(userId+" 抢单失败");
}

} finally {
//这种释放锁有可能释放了别人的锁。
//stringRedisTemplate.delete(lock.intern());
//下面代码避免释放别人的锁
if((userId+"").equals(stringRedisTemplate.opsForValue().get(lock.intern()))) {
stringRedisTemplate.delete(lock.intern());
}
}
return null;
}
}

Redis 配置类

1
2
3
4
5
6
7
8
9
10
@Component
public class RedisConfig {
@Bean
@ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate redisTemplate = new StringRedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}

需要注意的问题:

  • redis 集群里面的问题
    一般redis集群式 一主二从三哨兵

    如:两个用户买一个东西,库存100,买完应该是98

    A用户从主节点获取到锁之后,主节点挂掉了,但是数据没有及时同步到从节点,从节点升级为Master,用户B又会获取到对应的锁,这样A用户和B用户在减库从的时候都是从100 -1 的,所以会出现问题。

    可以考虑使用redisson

  • 如果锁没执行到释放,比如业务逻辑执行一半,运维重启服务,或 服务器挂了,没走 finally,怎么办?

    加超时事件,超时时间和setnx 应该用一行代码完成,因为两行代码时不具备原子性

  • 锁续期较为麻烦
    锁的时间长短比如是10秒,线程1 执行了12s,这个时候锁已经过期了,线程2就会抢占锁资源,当线程1 执行完业务逻辑的时候,释放锁的时候就会把线程2的锁释放掉,这个时候就应该开一个锁续期的线程,比如10s的锁超时时间,可以按照每3s的时间做一次续期操作,但是实际操作中较为复杂,不建议使用


分布式锁实现----Redis
http://yoursite.com/post/3646c2cc.html/
Author
Chase Wang
Posted on
November 26, 2021
Licensed under