如何用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 1 | int(1) | 
| return 0 | int(0) | 
| return true | int(1) | 
| return false | bool(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,是由第三个参数来决定的。那么这个数字变成别的值的时候又是什么行为呢?
| 第三个参数取值 | KEYS | ARGV | 
| -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);