如何用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);