Redis 脚本
Redis 脚本
Redis 脚本使用 Lua 解释器来执行脚本。 Redis 2.6 版本通过内嵌支持 Lua 环境。
关键词:
Lua
为什么使用 Lua
Lua 是一种轻量小巧的脚本语言,用标准 C 语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
在 Redis 中,执行单一命令是原子性操作,所以不会出现并发问题。但有的业务场景下,需要执行多个命令,同时确保不出现并发问题,这就需要用到 Lua 脚本了。
Redis 执行 Lua 是原子操作。因为 Redis 使用串行化的方式来执行 Redis 命令, 所以在任何特定时间里, 最多都只会有一个脚本能够被放进 Lua 环境里面运行, 因此, 整个 Redis 服务器只需要创建一个 Lua 环境即可。
由于,Redis 执行 Lua 具有原子性,所以常被用于需要原子性执行多命令的场景。
Redis 脚本命令
命令 | 说明 |
---|---|
EVAL |
EVAL 命令为客户端输入的脚本在 Lua 环境中定义一个函数, 并通过调用这个函数来执行脚本。 |
EVALSHA |
EVALSHA 命令通过直接调用 Lua 环境中已定义的函数来执行脚本。 |
SCRIPT_FLUSH |
SCRIPT_FLUSH 命令会清空服务器 lua_scripts 字典中保存的脚本, 并重置 Lua 环境。 |
SCRIPT_EXISTS |
SCRIPT_EXISTS 命令接受一个或多个 SHA1 校验和为参数, 并通过检查 lua_scripts 字典来确认校验和对应的脚本是否存在。 |
SCRIPT_LOAD |
SCRIPT_LOAD 命令接受一个 Lua 脚本为参数, 为该脚本在 Lua 环境中创建函数, 并将脚本保存到 lua_scripts 字典中。 |
SCRIPT_KILL |
SCRIPT_KILL 命令用于停止正在执行的脚本。 |
Redis 执行 Lua 的工作流程
为了在 Redis 服务器中执行 Lua 脚本, Redis 在服务器内嵌了一个 Lua 环境(environment), 并对这个 Lua 环境进行了一系列修改, 从而确保这个 Lua 环境可以满足 Redis 服务器的需要。
Redis 服务器创建并修改 Lua 环境的整个过程由以下步骤组成:
- 创建一个基础的 Lua 环境, 之后的所有修改都是针对这个环境进行的。
- 载入多个函数库到 Lua 环境里面, 让 Lua 脚本可以使用这些函数库来进行数据操作。
- 创建全局表格
redis
, 这个表格包含了对 Redis 进行操作的函数, 比如用于在 Lua 脚本中执行 Redis 命令的redis.call
函数。 - 使用 Redis 自制的随机函数来替换 Lua 原有的带有副作用的随机函数, 从而避免在脚本中引入副作用。
- 创建排序辅助函数, Lua 环境使用这个辅佐函数来对一部分 Redis 命令的结果进行排序, 从而消除这些命令的不确定性。
- 创建
redis.pcall
函数的错误报告辅助函数, 这个函数可以提供更详细的出错信息。 - 对 Lua 环境里面的全局环境进行保护, 防止用户在执行 Lua 脚本的过程中, 将额外的全局变量添加到了 Lua 环境里面。
- 将完成修改的 Lua 环境保存到服务器状态的
lua
属性里面, 等待执行服务器传来的 Lua 脚本。
Redis 执行 Lua 的要点
- Redis 服务器专门使用一个伪客户端来执行 Lua 脚本中包含的 Redis 命令。
- Redis 使用脚本字典来保存所有被
EVAL
命令执行过, 或者被SCRIPT_LOAD
命令载入过的 Lua 脚本, 这些脚本可以用于实现SCRIPT_EXISTS
命令, 以及实现脚本复制功能。 - 服务器在执行脚本之前, 会为 Lua 环境设置一个超时处理钩子, 当脚本出现超时运行情况时, 客户端可以通过向服务器发送
SCRIPT_KILL
命令来让钩子停止正在执行的脚本, 或者发送SHUTDOWN nosave
命令来让钩子关闭整个服务器。 - 主服务器复制
EVAL
、SCRIPT_FLUSH
、SCRIPT_LOAD
三个命令的方法和复制普通 Redis 命令一样 —— 只要将相同的命令传播给从服务器就可以了。 - 主服务器在复制
EVALSHA
命令时, 必须确保所有从服务器都已经载入了EVALSHA
命令指定的 SHA1 校验和所对应的 Lua 脚本, 如果不能确保这一点的话, 主服务器会将EVALSHA
命令转换成等效的EVAL
命令, 并通过传播EVAL
命令来获得相同的脚本执行效果。