相关链接

lua调试:http://redis.cn/topics/ldb.html

img

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 脚本。语法格式为:

  • *EVAL* *script numkeys key [key …] arg [arg …]*
  • *EVALSHA* *sha1 numkeys key [key …] arg [arg …]*

参数说明:

  • 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);
    }