码迷,mamicode.com
首页 > 其他好文 > 详细

《Redis设计与实现》学习笔记-Lua脚本

时间:2015-03-22 09:13:32      阅读:192      评论:0      收藏:0      [点我收藏+]

标签:redis   高性能分布式缓存   nosql   lua脚本   eval   

Redis从2.6开始支持Lua脚本,和事务的功能类似,可以通过Lua脚本原子的执行多个Redis命令。Redis提供了EVAL和EVALSHA命令执行lua脚本。

创建并修改Lua坏境

Redis在服务器内嵌了一个Lua坏境,并进行了一系列的修改,从而确保这个Lua坏境可以满足Redis服务器的需要,通过下列步骤创建并修改Lua坏境:

  1. 创建一个基础Lua坏境,通过调用Lua的C API函数lua_open。
  2. 载入多个函数库到Lua坏境中,让Lua脚本可以使用这些函数来进行数据操作。包括Lua核心函数(assert、error、pairs、tostring、pcall等)、表格库(比如table.concat、table.insert等)、字符串库(string.x)、数学库(math.x)、调试库(debug.x)、CJSON库、Struct库、cmsgpack库。
  3. 创建全局表格redis,该表格包含了对Redis进行操作的函数,比如用于在Lua脚本中执行Redis命令的redis.call和redis.pcall函数、用于记录日志的redis.log函数。
  4. 使用Redis自制的随机函数替换Lua原有的副作用随机函数,替换后的随机函数对于相同的seed总会产生相同的随机数序列。
  5. 创建排序辅助函数,Lua坏境使用这个辅助函数来对一部分Redis命令的结果进行排序,消除命令的不确定性,比如SMEMBERS这种命令多次执行可能会产生不同的结果,通过辅助函数来对结果进行排序让它们每次产生同样的结果。
  6. 创建redis.pcall函数的错误报告辅助函数,提供详细的出错信息。
  7. 对Lua坏境中的全局变量进行保护,防止用户在执行Lua脚本的过程中,将额外的变量添加到Lua坏境中,不过Redis没有禁止用户修改已存在的全局变量,所以使用Lua脚本的时候需要小心,以免错误的修改了已存在的全局变量。
  8. 将完成修改的Lua坏境保存到服务器状态的lua属性(redisServer结构的属性)中,等待执行服务器传来的Lua脚本。

Lua坏境协作组件

Redis服务器创建了两个用于与Lua坏境进行协作的组件,它们分别是负责执行Lua脚本中的Redis命令的伪客户端,以及用于保存Lua脚本的lua_scripts字典。

伪客户端

伪客户端负责处理Lua脚本中包含的Redis命令,Lua脚本使用redis.call或者redis.pcall函数执行一个Redis命令,需要以下步骤:
  1. Lua坏境将redis.call或者redis.pcall函数想要执行的命令传给伪客户端。
  2. 伪客户端将脚本想要执行的命令传给命令执行器。
  3. 命令执行器执行伪客户端传给它的命令,并将命令的执行结果返回给客户端。
  4. 伪客户端接收命令执行器返回的命令结果,并将这个命令结果返回给Lua坏境。
  5. Lua坏境在接收到命令结果之后,将该结果返回给redis.call或者redis.pcall。
  6. 接收到结果的redis.call或redis.pcall函数将命令结果作为函数返回值返回给脚本的调用者。

lua_scripts字典

作为一个属性存放在redisServer结构中,字典的键为Lua脚本的SHA1校验和,而字典的值时SHA1校验和对应的Lua脚本,Redis服务器会将所有被EVAL命令执行过的Lua脚本,以及所有被SCRIPT LOAD命令载入过的Lua脚本都保存到lua_scripts字典里面。

EVAL命令

EVAL命令实现分为三个步骤:
1、根据客户端给定的Lua脚本,在Lua坏境中定义一个Lua函数,函数名称由f_前缀加上脚本的SHA1校验和(四十个字符)组成,函数体为脚本本身。
2、将客户端给定的脚本保存到lua_scripts字典中。
3、执行步骤1中定义的函数,以此来执行客户端给定的Lua脚本,过程如下:

  • 将EVAL命令中传入的键名参数和脚本参数分别保存到KEYS数组和ARGV数组,然后将这两个数组作为全局变量传入到Lua坏境里面。
  • 为Lua坏境装载超时处理钩子,这个钩子可以在脚本出现超时运行的情况时,让客户端通过SCRIPT KILL命令停止脚本,或者通过SHUTDOWN命令直接关闭服务器。
  • 执行脚本函数。
  • 移除之前装载的超时钩子。
  • 将执行脚本函数所得的结果保存到客户端状态的输出缓冲区中,等待服务器将结果返回给客户端。
  • 对Lua坏境执行垃圾回收操作。

EVALSHA命令

每个被EVAL命令成功执行过的Lua脚本都会生成一个SHA1校验和,为了节省带宽资源,在下次执行相同的脚本时可以使用EVALSHA命令,把之前生成的校验和传到服务器就可以了。

脚本管理命令

  • SCRIPT FLUSH,用于清除服务器中所有和Lua脚本有关的信息:释放lua_scripts字典,关闭现有的Lua坏境并重新创建一个Lua坏境。
  • SCRIPT EXISTS,根据输入的SHA1校验和,检查对应的脚本是否存在,通过检查lua_scripts字典。
  • SCRIPT LOAD,将脚本保存到lua_scripts字典中。
  • SCRIPT KILL,当服务器设置了lua-time-limit配置选项,每次执行lua脚本之前,服务器都会在Lua坏境中设置一个超时钩子,超时钩子在脚本运行期间检查,一旦脚本的执行时间超过了lua-time-limit,钩子将定期在脚本运行的间隙中查看是否有SCRIPT KILL或者SHUTDOWN命令。如果超时的脚本未执行过任何写入操作,那么客户端可以通过SCRIPT KILL来指示服务器停止执行这个脚本,如果执行过写入操作,客户端只能用SHUTDOWN nosave命令停止服务器,防止不合法的数据写入数据库。

脚本复制

当服务器运行在复制模式下,EVAL命令、SCRIPT FLUSH、SCRIPT LOAD、EVALSHA命令会复制给从服务器,除了EVALSHA命令,其它三个命令都是直接传播,在传播EVALSHA命令,先检查从服务器是否加载过这个命令,如果加载过了直接复制,如果未加载过先要把EVALSHA命令转换成等价的EVAL命令再传播。

《Redis设计与实现》学习笔记-Lua脚本

标签:redis   高性能分布式缓存   nosql   lua脚本   eval   

原文地址:http://blog.csdn.net/pentiumchen/article/details/44523069

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!