redis distributed lock

181 阅读2分钟

仅作为笔记

SETNX

SETNX KEY_NAME VALUE 在指定的 key 不存在时,为 key 设置指定的值。

返回值

设置成功,返回 1 。 设置失败,返回 0

GETSET

GETSET KEY_NAME VALUE 设置指定 key 的值,并返回 key 的旧值。

返回值

返回给定 key 的旧值。 当 key 没有旧值时,即 key 不存在时,返回 nil 。

当 key 存在但不是字符串类型时,返回一个错误。

redis执行redis脚本

参数说明

EVAL script numkeys key [key ...] arg [arg ...]

script: 参数是一段 Lua 5.1 脚本程序。脚本不必(也不应该)定义为一个 Lua 函数。 numkeys: 用于指定键名参数的个数。 key [key ...]: 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。 arg [arg ...]: 附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。

方法一: SETNX(不推荐)

加锁(非原子):

SETNX lock <current Unix time + lock timeout>

返回 1,客户端加锁成功
返回 0,客户端加锁失败,代表key已经被设置过

  1. GET lock 检查锁是否过期:
  2. 没有过期则sleep一会并retry
  3. 如果已经过期,则尝试获取锁. 调用GETSET lock <current Unix time + lock timeout> 基于当前时间设置过期时间.注意: 这里因为SETNXGETSET之间有窗口期,所以在这个期间内有可能被别的客户端抢走锁,所以要判断GETSET的返回值(之前设置的旧的时间戳,其实这里是有空窗期的,会导致判断GETSET的返回值的时候,虽然是过期的,同时别的客户端也会判断是过期的)
  • 若旧的时间戳已经过期,则代表加锁成功
  • 若还未过期(说明这期间被其他客户端抢占并更新了时间戳),加锁失败,需要等待重试

解锁(非原子):

GET lock 判断是否过期,过期则DEL lock (但是GETDEL之前的空窗期可能别的锁已经获得锁了,在这里调用DEL则在别的客户端不知情的情况下解除锁的占用)

方法二: SET NX PX

加锁(原子):

SET key unique_value NX PX 1000 时间单位毫秒,只有在key不存在才会执行成功 (unique_value是唯一的,保证自己的锁只能自己释放)

解锁(原子):

eval "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end" 1 key unique_value

if redis.call('get',KEYS[1]) == ARGV[1] then
    return redis.call('del',KEYS[1])
else
    return 0 
 

参数为 1 key random_value