先了解Set参数
SET key value [EX seconds] [PX milliseconds] [NX|XX]
将字符串值value关联到key
对于某个原本带有生存时间(TTL)的键来说, 当SET命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。
可选参数
命令的行为可以通过一系列参数来修改:- EX second:设置键的过期时间为second秒。SET key value EX second 效果等同 SETEX key second value。
- PX millisecond:设置键的过期时间为millisecond毫秒。SET key value PX millisecond效果等同于PSETEX key millisecond value。
- NX:只在键不存在时,才对键进行设置操作。SET key value NX 效果等同于 SETNX key value。
- XX:只在键已经存在时,才对键进行设置操作。
返回值:
命令总是返回 OK。
从 Redis 2.6.12 版本开始,
SET
在设置操作成功完成时,才返回OK。如果设置了NX 或者 XX,但因为条件没达到而造成设置操作未执行,那么命令返回空批量回复(NULL Bulk Reply)。
setnx
锁在redis中最简单的数据结构就是string。最早的时候,上锁的操作一般使用setnx,这个命令是当锁不存在的时候set一个val,或许你还会记得使用expire来增加锁的过期,解锁操作就是使用del命令,代码如下:
if(redis.setnx('node:lock', 1)){
redis.expire('node:lock',10);
// ...todo
redis.del('node:lock')
}
}
问题就在于setnx和expire中间如果遇到crash等行为,可能这个lock就不会被释放了
set
为了解决这个问题,我们可以把超时时间设置跟set操作放在一起
if(redis.set('node:lock',1, 'nx', 'ex', 10)){
// ...todo
redis.del('node:lock')
}
}
获取锁的机制是对了,但是删除锁的机制直接使用del是不对的。因为有可能导致误删别人的锁的情况。
比如,这个锁我上了10s,但是我处理的时间比10s更长,到了10s,这个锁自动过期了,被别人取走了,并且对它重新上锁了。那么这个时候,我再调用del就是删除别人建立的锁了。
终极版:
function lock(lockName,lockTimeout)){
let lockKey=lockName+':lock';
let lockValue=new Date().getTime();
if(redis.set(lockKey,lockValue, 'nx', 'ex', lockTimeout)){
return lockValue;
}
return null;
}
function unlock(lockName, lockValue)){
let lockKey=lockName+':lock';
if(redis.get(lockKey)===lockValue){ redis.del(lockName)
return true;
}}
这里的lockValue是一个时间戳,当lock的时候,往redis的存储这个时间戳,unlock的时候,先get一下lock中的值,如果和我要删除的值是一致的,说明这个锁是之前我set的,否则的话,说明这个锁已经过期,是别人set的,我就不应该对它进行任何操作。