鱼喃

听!布鲁布鲁,大鱼又在那叨叨了

redis入门(6)---利用lua脚本扩展命令

编写lua脚本来扩展redis命令

为什么使用脚本

  • 原有的命令集合不够丰富
  • redis命令是原子操作,避免竞态。虽然事务也可以保证原子性,但一些情况下无法使用事务完成,如根据get的值选择不同的操作
  • 常用命令写成脚本,避免多种应用(不同语言等等)编写重复代码

lua脚本(实现频率限制)

1
2
3
4
5
6
7
8
9
10
11
local times = redis.call('incr', KEYS[1])
if times == 1 then
-- KEYS[1]键刚建立,为其设置生存周期
redis.call('expire', KEYS[1], ARGV[1])
end

if times > tonumber(ARGV[2]) then
return 0
end

return 1

使用脚本

在命令行运行

EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 foo bar 输出OK ,与 SET foo bar 一样

在php中使用

注意:网上很多教程中使用的接口为

1
eval($lua_script, array('key, 'argv'), 1)

或者

1
evalSha($sha, array('key', 'argv'), 1)

根据测试发现,最新版本的predis已经更改了接口函数,目前的调用方式为

1
eval($lua_script, 1, 'key', 'argv')

或者

1
evalSha($sha, 1, 'key', 'argv')

,一言不和就修改接口函数,这得坑到多少小朋友啊

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
require_once('./predis-1.0.3/autoload.php');

function get_connection(){
try{
$redis = new Predis\Client(array(
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => 6379
));
return $redis;
} catch (Exception $e){
echo "Error: {$e->getMessage()}";
return null;
}
}

function can_login($ip){
$lua_script = <<<LUA
local times = redis.call('incr', KEYS[1])
if times == 1 then
-- KEYS[1]
redis.call('expire', KEYS[1], ARGV[1])
end

if times > tonumber(ARGV[2]) then
return 0
end
return 1
LUA;

$redis = get_connection();
//1 表示后面有一个keys参数,其余是argv参数
//通过eval执行脚本
return $redis->eval($lua_script, 1, 'visit:'.$ip, '60', 5) == 1;
//或者先加载脚本,获得sha1,再执行evalSha
//$sha = $redis->script("load", $lua_script);
//return $redis->evalSha($sha, 1, 'visit:'.$ip, '60', '5') == 1;
}

//查看127.0.0.1的ip是否可以再次尝试登录
var_dump(can_login(ip2long('127.0.0.1')));
?>