分布式锁

[TOC]

1.使用互斥锁(mutex key)

业界比较常用的做法,是使用mutex。简单地来说。

SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public String get(key) {
String value = redis.get(key);
if (value == null) {
//代表缓存值过期
//设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
if (redis.setnx(key_mutex, 1, 3 * 60) == 1) {
//代表设置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key_mutex);
} else {
//这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
sleep(50);
get(key);
//重试
}
} else {
return value;
}
}

2.zookeeper

创建一个锁目录 /lock;
当一个客户端需要获取锁时,在 /lock 下创建临时的且有序的子节点;
客户端获取 /lock 下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是
则认为获得锁;否则监听自己的前一个子节点,获得子节点的变更通知后重复此步骤直至获得锁;
执行业务代码,完成后,删除对应的子节点。

3.Redis 的 RedLock 算法

使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时仍然可用。
尝试从 N 个互相独立 Redis 实例获取锁;
计算获取锁消耗的时间,只有当这个时间小于锁的过期时间,并且从大多数(N / 2 + 1)实例上获取了锁,那
么就认为锁获取成功了;
如果锁获取失败,就到每个实例上释放锁。

区别

①redis分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能

②zk分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小

③另外一点就是,如果是redis获取锁的那个客户端bug了或者挂了,那么只能等待超时时间之后才能释放锁;而zk的话,因为创建的是临时znode,只要客户端挂了,znode就没了,此时就自动释放锁