相关链接
lua调试:http://redis.cn/topics/ldb.html
Lua脚本优势
1、减少网络开销:可以将多个请求通过脚本的形式一次发送,减少网络时延和请求次数。
2、原子性的操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。
3、代码复用:客户端发送的脚步会永久存在redis中,这样,其他客户端可以复用这一脚本来完成相同的逻辑。
4、速度快:见 与其它语言的性能比较, 还有一个 JIT编译器可以显著地提高多数任务的性能; 对于那些仍然对性能不满意的人, 可以把关键部分使用C实现, 然后与其集成, 这样还可以享受其它方面的好处。
5、可以移植:只要是有ANSI C 编译器的平台都可以编译,你可以看到它可以在几乎所有的平台上运行:从 Windows 到Linux,同样Mac平台也没问题, 再到移动平台、游戏主机,甚至浏览器也可以完美使用 (翻译成JavaScript).
6、源码小巧:20000行C代码,可以编译进182K的可执行文件,加载快,运行快。
lua 脚本
数据类型 | 描述 |
---|---|
nil | 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。 |
boolean | 包含两个值:false和true。 |
number | 表示双精度类型的实浮点数。 |
string | 字符串由一对双引号或单引号来表示。 |
function | 由 C 或 Lua 编写的函数 |
userdata | 表示任意存储在变量中的C数据结构 |
thread | 表示执行的独立线路,用于执行协同程序 |
table | Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字或者是字符串。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表 local tbl1 = {}。 |
Lua的变量分为全局变量和局部变量。全局变量无需声明就可以直接使用,默认值是nil。如:
a = 5 -- 为全局变量a赋值
print(b) -- 无需声明即可使用,默认是nil
a = nil -- 删除全局变量a的方法是将其赋值为nil,全局变量没有声明和未声明之分,只有nil和非nil的区别
而在Redis脚本中不能使用全局变量,只允许使用局部变量以防止脚本之间相互影响。用 local 显示声明为局部变量。
local c -- 声明一个局部变量,默认值是nil
local d=1 -- 声明一个局部变量d并赋值为1
local e,f -- 可以同时声明多个局部变量
同时声明一个存储函数的局部变量的方法为:
local say_hi = function()
print 'hi'
end
变量名必须是非数字开头,只能包含字母、数字和下划线,区分大小写。变量名不能与Lua的保留关键字相同。 局部变量的作用域为从声明开始到所在层的语句块末尾,比如:
local x=10
if true then
local x=x+1
print(x)
do
local x=x+!
print(x)
end
end
print(x)
命令格式
Redis 提供了 EVAL(直接执行脚本) 和 EVALSHA(执行 SHA1 值的脚本) 这两个命令,可以使用内置的 Lua 解析器执行 Lua 脚本。语法格式为:
参数说明:
- script / sha1:EVAL 命令的第一个参数为需要执行的 Lua 脚本字符,EVALSHA 命令的一个参数为 Lua 脚本的 SHA1 值
- numkeys:表示 key 的个数
- key [key …]:从第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局数组 KYES[i] 访问
- arg [arg …]:附加参数,在 Lua 中通过全局数组 ARGV[i] 访问
EVAL 命令的使用示例:
> EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"
每次使用 EVAL 命令都会传递需执行的 Lua 脚本内容,这样增加了宽带的浪费。Redis 内部会永久保存被运行在脚本缓存中,所以使用 EVALSHA(建议使用) 命令就可以根据脚本 SHA1 值执行对应的 Lua 脚本。
> SCRIPT LOAD "return 'hello'"
"1b936e3fe509bcbc9cd0664897bbe8fd0cac101b"
> EVALSHA 1b936e3fe509bcbc9cd0664897bbe8fd0cac101b 0
"hello"
批量HGETTALL
这个例子演示通过 Lua 实现批量 HGETALL
-- KEYS为uid数组
local users = {}
for i,uid in ipairs(KEYS) do
local user = redis.call('hgetall', uid)
if user ~= nil then
table.insert(users, i, user)
end
end
return users
应用场景
1、防止DDOS防护:限制n秒内同IP的访问次数
2、游戏热更新
laravel 用例测试
/**
* 修改人员
*
* @param level 用户等级标识
* @param int suid 原始值
* @param int euid 修改值
*/
public function updateUser($level, int $suid, int $euid)
{
$key = $this->processUserKey . ':' . $level;
$script = <<<LUA_SCRIPT
local delete = redis.call('srem', KEYS[1], ARGV[1])
local insert = redis.call('sadd', KEYS[2], ARGV[2])
if (delete == 1 and insert) == 1
then
return 1
else
return 0
end
LUA_SCRIPT;
return Redis::eval($script, 2, $key, $key, $suid, $euid);
}