# Redis之分布式锁
redis 分布式锁一般是使用 setnx(set if not exists) 指
保证因为异常不会死锁,必须增加过期时间,而且需要原子性操作,采用如下命令。
> set lock true ex 5 nx
1
# 以上方案集群缺陷
比如在 Sentinel 集群中,主节点挂掉时,从节点会取而代之,客户端上却并没有明显感知。原先第一个客户端在主节点中申请成功了一把锁,但是这把锁还没有来得及同步到从节点,主节点突然挂掉了。然后从节点变成了主节点,这个新的节点内部没有这个锁,所以当另一个客户端过来请求加锁时,立即就批准了。这样就会导致系统中同样一把锁被两个客户端同时持有,不安全性由此产生。
比如A请求申请了一把锁,但是A由于延迟操作导致锁过期了,B重新拿到了锁,A操作完了之后不知道自己的锁过期了,在去释放锁,就会把B的锁给释放掉,所以redis的value也需要维护。
# Redlock 算法
为了解决这个问题,Redis官网 发明了 Redlock 算法,它的流程比较复杂,不过已经有了很多开源的 library 做了良好的封装,用户可以拿来即用,比如 node-redlock (opens new window)。
在Nodejs中的使用参考
'use strict';
const ioredis = require('ioredis');
const config = require('../../config');
config.redis.db = 17;
const redis = new ioredis(config.redis);
const Redlock = require('redlock');
const redlock = new Redlock(
[ redis ],
{ retryDelay: 200, // time in ms
retryCount: 50,
}
);
function sleep(time = 2000) {
return new Promise(resolve => {
setTimeout(function() {
resolve();
}, time);
});
}
const demo = async () => {
const resource = 'key';
const ttl = 10000; // 10s
const reqId = parseInt(Math.random() * 100000000);
console.log(reqId, 1);
const data = await redlock.lock(resource, ttl); // 加锁
console.log(reqId, 2);
await sleep();
data.unlock();
console.log(reqId, 3);
};
const run = async () => {
demo();
demo();
demo();
};
run();
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
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