Redis 分布式锁 解决集群环境下多次定时任务执行

1,700 阅读2分钟

先了解Set参数

SET key value [EX seconds] [PX milliseconds] [NX|XX]

将字符串值value关联到key

如果key有值, SET 就覆写旧值,无视类型。

对于某个原本带有生存时间(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:只在键已经存在时,才对键进行设置操作。
返回值:

在 Redis 2.6.12 版本以前,

命令总是返回 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的,我就不应该对它进行任何操作。