Redis 分布式锁

Redis 分布式锁

分布式锁就是在分布式环境下获取锁,实现进程以独占资源的方式访问共享资源。

Redis 分布式锁的特性

  • 独享。同一时刻,只能有一个客户端获取到锁。
  • 无死锁。即使持有锁的客户端奔溃或网络断开,锁仍然可以被获取。
  • 容错。只要大部分Redis节点都活着,客户端就可以获取和释放锁。

单 Redis 实例实现分布式锁实现

SET 命令

1
2
3
4
5
SET key value [EX seconds] [PX milliseconds] [NX|XX]
# EX seconds – 设置键key的过期时间,单位时秒
# PX milliseconds – 设置键key的过期时间,单位时毫秒
# NX – 只有键key不存在的时候才会设置key的值
# XX – 只有键key存在的时候才会设置key的值

获取锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 获取 分布式锁
* @param lockName 锁的 key
* @return 成功时返回一个随机生成的id,为了正确的释放锁,失败时返回 null
*/
public static String getLock(String lockName) {
String result = null;
try (Jedis jedis = getJedis()) {
String randomId = UUID.randomUUID().toString();
// EX seconds – 设置键key的过期时间,单位时秒。PX milliseconds – 设置键key的过期时间,单位时毫秒。NX – 只有键key不存在的时候才会设置key的值。XX – 只有键key存在的时候才会设置key的值
String status = jedis.set(lockName, randomId, "nx", "px", 10000); // 10s 过期时间
if ("OK".equals(status)) {
result = randomId;
}
} catch (Exception e) {
String msg = ExceptionUtil.stackTraceMsg(e);
logger.error(msg);
}
return result;
}

释放锁

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
/**
* 释放锁
* @param lockName 锁的名称
* @param randomId 获取锁时返回的的id
* @return
*/
public static boolean releaseLock(String lockName, String randomId) {
boolean result = false;
// Lua 脚本
String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
" return redis.call(\"del\",KEYS[1])\n" +
"else\n" +
" return 0\n" +
"end";
try (Jedis jedis = getJedis()) {
Long status = (Long) jedis.eval(script, Collections.singletonList(lockName), Collections.singletonList(randomId));
if (status.longValue() == 1L) {
result = true;
}
} catch (Exception e) {
String msg = ExceptionUtil.stackTraceMsg(e);
logger.error(msg);
}
return result;
}

单 Redis 实例实现分布式锁存在的问题

在单 Redis 实例的分布式锁实现中存在的最严重的问题就是单节点失败的问题,不能保证 Redis 永远不会挂掉。
在主从结构的 Redis 集群中,这种构架也是有问题的,不能实现资源的独享,因为 Redis 的主从同步通常是异步的:

  1. 客户端 A 从 Master 获取到锁
  2. 在 Master 同步到 Slave 之前,Master 节点挂掉了
  3. Slave 节点成为了 Master 节点
  4. 客户端B取得了同一个资源被客户端A已经获取到的另外一个锁。安全失效!

Redis 宕机毕竟是小概率实现,所以在可以忽略宕机的事件时,这个构架还是很实用的。

Redlock

Redlock 是 Antirez 提出的,使用多个完全独立的 Redis 实例,来实现分布式锁的算法。

参考:The Redlock algorithm
参考:Redis集群下的RedLock算法(真分布式锁) 实践

Redisson 分布式锁

参考:分布式锁和同步器