Redis 实现分布式锁

Redis 实现

Redis 分布式锁的实现主要基于 SETNX key value 这个指令

  • 如果 key 不存在,就创建对应的键值对,创建成功,返回 1
  • 如果 key 存在,创建失败,返回 0
1
2
3
4
5
6
> setnx 1 xin
(integer) 1
> setnx 1 xin2
(integer) 0
> setnx 2 xin
(integer) 1

除此之外还需要解决键值对长期有效的问题,还需要为键值对设置一个过期时间,对应的 redis 指令为 EXPIRE key seconds,到期之后键值会自动删除

1
2
3
4
5
6
7
> get 1
"xin"
> expire 1 2
(integer) 1
// 2秒后
> get 1
(nil)

在 redis 2.6.12 以后的版本,可以用组合指令来实现上述的分布式锁
SET key value [EX seconds] NX

1
2
3
4
5
6
7
8
9
> set 1 xin ex 10 nx
OK
> get 1
"xin"
> ttl 1
(integer) 4
// 10秒后
> ttl 1
(integer) -2

ttl key 命令用于查看键值对的剩余过期时间

  • 对于设置了过期时间并且尚未过期的键值对,ttl 命令返回剩余过期时间;
  • 对于已过期不存在的键值对,ttl 命令返回 -2;
  • 对于没有过期时间的键值对,ttl 命令返回 -1;

Jedis 实现

在 Jedis 中可以实现一个 setNxAndExp 方法来实现分布式锁的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public Integer setNxAndExp(String key, String val, int expSecond) {
// 如果键值对创建成功,设置一个过期时间
if (jedisCluster.setNx(key, val) > 0) {
if (expSecond > 0) {
jedisCluster.expire(key, expSecond);
}
return 1;
} else {
// 如果创建失败,并且已有的键值对没有过期时间,为其设置一个过期时间,一段时间后释放锁
if (jedisCluster.ttl(key).longValue() == -1) {
jedisCluster.expire(key, expSecond);
}
}
// 创建成功返回 1,创建失败返回 0,与 setNx 返回值相同
return 0;
}

参考

redis 基于SETNX和EXPIRE的用法 实现redis 分布式锁