在PHP中使用Redis的lua脚本


如何用Redis来实现一个正确的分布式锁呢?最近面试,经常会问候选人这个问题,基本也没有人能答对。

加锁操作:set(key, random, [nx, ex])

解锁操作:

eval(
  if (get(key) == random){
    del(key)
    return 1
  } else { 
    return 0 
  }
)

以上代码当然不是正确的lua代码了,这就是这篇文章的目的:探究一下,在PHP中如何使用在Redis中执行的lua脚本。

$result = $redis->eval(<<<LUA
if redis.call('get', KEYS[1]) == ARGV[1] 
then 
    return redis.call('del', KEYS[1]) 
else 
    return false
end
LUA
        , [$key, $ticket], 1) !== false;

如上这段就是我初步写的代码,但是,我要怎么验证它对不对呢?通过做一些实验来验证下吧。

lua脚本写的返回语句PHP得到的返回值
return 1int(1)
return 0int(0)
return trueint(1)
return falsebool(false)

可以看到,我们用bool是不太科学的(没有返回true的时候),所以,我们还是尝试用标准的0/1来做返回值吧~

然后,我们再来看一下KEYS和ARGV在PHP中是如何使用的,验证lua代码如下:

$code = <<<LUA
return {KEYS,ARGV}
LUA;

$r = $redis->eval($code, ['a', 'b', 'c', 'd'], 2);
var_dump($r);

得到的结果是:

array(2) {
   [0] =>
   array(2) {
     [0] =>
     string(1) "a"
     [1] =>
     string(1) "b"
   }
   [1] =>
   array(2) {
     [0] =>
     string(1) "c"
     [1] =>
     string(1) "d"
   }
 }

KEYS和ARGV是放在同一个数组中传递给lua脚本的,哪些是KEYS,是由第三个参数来决定的。那么这个数字变成别的值的时候又是什么行为呢?

第三个参数取值KEYSARGV
-4-(猜测溢出了,服务器被搞死了。redis:v2.8)
-3[][EVAL, return {KEYS,ARGV}, -3, a, b, c, d]
-2[] [return {KEYS,ARGV}, -2, a, b, c, d]
-1[] [-1, a, b, c, d]
0[][a, b, c, d]
1[a][b, c, d]
5-(eval函数返回false)

知道了这些,就可以愉快的编程了~ 如下就是PHP中解锁操作的代码了

$lua = <<<LUA
if redis.call('get', KEYS[1]) == ARGV[1] 
then 
    return redis.call('del', KEYS[1])
else 
    return 0
end
LUA;
return (bool)$redis->eval($lua, [$key, $ticket], 1);


发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注