中山百度网站排名,微网站建设公司首选,域名备案期间 网站访问,做视频网站需要哪些手续文章目录 创建并修改Lua环境Lua环境协作组件伪客户端lua scripts字典 EVAL命令的实现定义脚本函数执行脚本函数 EVALSHA命令的实现脚本管理命令的实现SCRIPT FLUSHSCRIPTEXISTSSCRIPT LOADSCRIPT KILL 脚本复制复制 EVAL命令、SCRIPT FLUSH命令和SCRIPT LOAD命令* 复制EVALSHA命… 文章目录 创建并修改Lua环境Lua环境协作组件伪客户端lua scripts字典 EVAL命令的实现定义脚本函数执行脚本函数 EVALSHA命令的实现脚本管理命令的实现SCRIPT FLUSHSCRIPTEXISTSSCRIPT LOADSCRIPT KILL 脚本复制复制 EVAL命令、SCRIPT FLUSH命令和SCRIPT LOAD命令* 复制EVALSHA命令1.判断传播EVALSHA命令是否安全的方法清空repl_scriptcache_dict字典传播EVALSHA命令的方法 总结 Redis从2.6版本开始引人对Lua脚本的支持通过在服务器中嵌入Lua环境Redis客户端可以使用Lua脚本直接在服务器端 原子地执行多个Redis命令。
创建并修改Lua环境
为了在Redis服务器中执行Lua脚本Redis在服务器内嵌了一个Lua环境environ-ment并对这个Lua环境进行了一系列修改从而确保这个Lua环境可以满足Redis服务器的需求。 Redis服务器创建并修改Lua环境的整个过程由以下步骤组成
1创建一个基础的Lua环境之后的所有修改都是针对这个环境进行的。2载人多个函数库到Lua环境里面让Lua脚本可以使用这些函数库来进行数据操作。3创建全局表格redis这个表格包含了对Redis进行操作的函数比如用于在Lua脚本中执行Redis命令的redis.call函数。4使用Redis自制的随机函数来替换Lua原有的带有副作用的随机函数从而避免在脚本中引人副作用。5创建排序辅助函数Lua环境使用这个辅佐函数来对一部分Redis命令的结果进行排序从而消除这些命令的不确定性。6创建redis.pcall函数的错误报告辅助函数这个函数可以提供更详细的出错信息。7对Lua环境中的全局环境进行保护防止用户在执行Lua脚本的过程中将额外的全局变量添加到Lua环境中。8将完成修改的Lua环境保存到服务器状态的lua属性中等待执行服务器传来的Lua脚本。
Lua环境协作组件
除了创建并修改Lua环境之外Redis服务器还创建了两个用于与Lua环境进行协作的组件它们分别是负责执行Lua脚本中的Redis命令的伪客户端以及用于保存Lua脚本的lua_scripts字典。
伪客户端
因为执行Redis命令必须有相应的客户端状态所以为了执行Lua脚本中包含的Redis命令Redis服务器专门为Lua环境创建了一个伪客户端并由这个伪客户端负责处理Lua脚本中包含的所有Redis命令。
Lua脚本使用redis.call函数或者redis.pcall 函数执行一个Redis命令需要完成以下步骤
1Lua环境将redis.call函数或者redis.pcall 函数想要执行的命令传给伪客户端。2伪客户端将脚本想要执行的命令传给命令执行器。3命令执行器执行伪客户端传给它的命令并将命令的执行结果返回给伪客户端。4伪客户端接收命令执行器返回的命令结果并将这个命令结果返回给Lua环境。5Lua环境在接收到命令结果之后将该结果返回给redis.call函数或者redis.pcall函数。6接收到结果的redis.call函数或者redis.pcall 区函数会将命令结果作为函数返回值返回给脚本中的调用者。
lua scripts字典
Redis服务器为Lua环境创建的另一个协作组件是lua_scripts字典这个字典的键为某个Lua脚本的SHAI校验和checksum而字典的值则是SHA 校验和对应的Lua脚本 struct redisServer {
dict *lua_scripts ;
} Redis服务器会将所有被EVAL命令执行过的Lua脚本以及所有被SCRIPTLOAD命令载人过的Lua脚本都保存到 lua_scripts 字典里面。
lua_scripts字典有两个作用一个是实现SCRIPTEXISTS命令另一个是实现脚本复制功能。
EVAL命令的实现
EVAL命令的执行过程可以分为以下三个步骤
1根据客户端给定的Lua脚本在Lua环境中定义一个Lua函数。2将客户端给定的脚本保存到lua_scripts字典等待将来进一步使用。3执行刚刚在Lua环境中定义的函数以此来执行客户端给定的Lua脚本。
定义脚本函数
当客户端向服务器发送EVAL命令要求执行某个Lua脚本的时候服务器首先要做的就是在Lua环境中为传入的脚本定义一个与这个脚本相对应的Lua函数其中Lua函数的名字由f育前级加上脚本的SHA1校验和四十个字符长组成而函数的体body则是脚本本身。
使用函数来保存客户端传人的脚本有以下好处
执行脚本的步骤非常简单只要调用与脚本相对应的函数即可通过函数的局部性来让Lua环境保持清洁减少了垃圾回收的工作量并且避免了使用全局变量。如果某个脚本所对应的函数在Lua环境中被定义过至少一次那么只要记得这个脚本的SHA1校验和服务器就可以在不知道脚本本身的情况下直接通过调用Lua函数来执行脚本这是EVALSHA命令的实现原理稍后在介绍EVALSHA命令的实现时就会说到这一点。
执行脚本函数
在为脚本定义函数并且将脚本保存到lua_scripts字典之后服务器还需要进行一些设置钩子、传入参数之类的准备动作才能正式开始执行脚本。整个准备和执行脚本的过程如下
1将EVAL命令中传人的键名keyname参数和脚本参数分别保存到KEYS数组和ARGV数组然后将这两个数组作为全局变量传人到Lua环境里面。2为Lua环境装载超时处理钩子hook这个钩子可以在脚本出现超时运行情况时让客户端通过SCRIPTKLLL命令停止脚本或者通过SHUTDOWN命令直接关闭服务器。3执行脚本函数。4移除之前装载的超时钩子。5将执行脚本函数所得的结果保存到客户端状态的输出缓冲区里面等待服务器将结果返回给客户端。6对Lua环境执行垃圾回收操作。
EVALSHA命令的实现
根据lua脚本的SHA1校验值执行对应的lua脚本。如果存在则执行不存在则报错。
脚本管理命令的实现
SCRIPT FLUSH
SCRIPT FLUSH命令用于清除服务器中所有和Lua脚本有关的信息这个命令会释放并重建lua_scripts字典关闭现有的Lua环境并重新创建一个新的Lua环境。
SCRIPTEXISTS
SCRIPT EXISTS 命令根据输人的SHA1校验和检查校验和对应的脚本是否存在于服务器中
SCRIPT LOAD
SCRIPT LOAD命令所做的事情和EVAL命令执行脚本时所做的前两步完全一样命令首先在Lua环境中为脚本创建相对应的函数然后再将脚本保存到lua_scripts字典里面。
SCRIPT KILL
如果服务器设置了lua-time-limit配置选项那么在每次执行Lua脚本之前服务器都会在Lua环境里面设置一个超时处理钩子hook。
超时处理钩子在脚本运行期间会定期检查脚本已经运行了多长时间一旦钩子发现脚本的运行时间已经超过了lua-time-limit选项设置的时长钩子将定期在脚本运行的间隙中查看是否有SCRIPT KLLL命令或者SHUTDOWN命令到达服务器。
如果超时运行的脚本未执行过任何写人操作那么客户端可以通过SCRIPT KLL命令来指示服务器停止执行这个脚本并向执行该脚本的客户端发送一个错误回复。处理完SCRIPT KILL命令之后服务器可以继续运行。
另一方面如果脚本已经执行过写人操作那么客户端只能用SHUTDOWN nosave命令来停止服务器从而防止不合法的数据被写人数据库中。
脚本复制
与其他普通Redis命令一样当服务器运行在复制模式之下时具有写性质的脚本命令也会被复制到从服务器这些命令包括EVAL命令、EVALSHA命令、SCRIPTFLUSH命令 以及SCRIPTLOAD 命令。
复制 EVAL命令、SCRIPT FLUSH命令和SCRIPT LOAD命令
Redis复制EVAL、SCRIPT FLUSH、SCRIPT LOAD三个命令的方法和复制其他普通Redis命令的方法一样当主服务器执行完以上 三个命令的其中一个时主服务器会直接将被执行的命令传播propagate给所有从服务器。
* 复制EVALSHA命令
EVALSHA4命令是所有与Lua脚本有关的命令中复制操作最复杂的一个因为主服务器与从服务器载入Lua脚本的情况可能有所不同所以主服务器不能像复制EVAL命令、SCRIPT LOAD命令或者SCRIPT FLUSH命令那样直接将EVALSHA命令传播给从服务器。对于一个在主服务器被成功执行的EVALSHA命令来说相同的EVALSHA命令在从服务器执行时却可能会出现脚本未找到not found错误。
因为多个从服务器之间载入Lua脚本的情况也可能各有不同所以即使一个EVALSHA命令可以在某个从服务器成功执行也不代表这个EVALSHA命令就一定可以在另一个从服务器成功执行。
为了防止以上假设的情况出现Redis要求主服务器在传播EVALSHA命令的时候必须确保EVALSHA命令要执行的脚本已经被所有从服务器载人过如果不能确保这一点的话主服务器会将EVALSHA命令转换成一个等价的EVAL命令然后通过传播EVAL命令来代替EVALSHA命令。
传播EVALSHA命令或者将EVALSHA命令转换成EVAL命令都需要用到服务器状态的lua_scripts字典和repl_scriptcache_dict字典。
1.判断传播EVALSHA命令是否安全的方法
主服务器使用服务器状态的repl_scriptcache_dict字典记录自己已经将哪些脚本传播给了所有从服务器。
repl_scriptcache_dict字典的键是一个个Lua脚本的SHAI校验和而字典的值则全部都是NULL当一个校验和出现在repl_scriptcache_dict字典时说明这个校验和对应的Lua脚本已经传播给了所有从服务器主服务器可以直接向从服务器传播包含这个SHA1校验和的EVALSHA命令而不必担心从服务器会出现脚本未找到错误。
如果一个脚本的SHAI校验和存在于lua.scriptss字典但是却不存在于repl_scriptcache_dict字典那么说明校验和对应的Lua脚本已经被主服务器载入但是并没有传播给所有从服务器如果我们尝试向从服务器传播包含这个SHA1校验和的EVALSHA命令那么至少有一个从服务器会出现脚本未找到错误。所以需要转化为EVAL命令。
清空repl_scriptcache_dict字典
每当主服务器添加一个新的从服务器时主服务器都会清空自已的repl_scriptcache_dict字典这是因为随着新从服务器的出现repl_scriptcache_dict字典里面记录的脚本已经不再被所有从服务器载人过所以主服务器会清空repl_scriptcache_dict字典强制自己重新向所有从服务器传播脚本从而确保新的从服务器不会出现脚本未找到错误。
传播EVALSHA命令的方法
当主服务器成功在本机执行完一个EVALSHA命令之后它将根据EVALSHA命令指定的SHA1校验和是否存在于repl_scriptcache_dict字典来决定是向从服务器传播EVALSHA命令还是EVAL命令
1如果EVALSHA命令指定的SHA1校验和存在于repl_scriptcache_dict字典那么主服务器直接向从服务器传播EVALSHA命令。2如果EVALSHA命令指定的SHA1校验和不存在于repl_scriptcache_dict字典那么主服务器会将EVALSHA命令转换成等价的EVAL命令然后传播这个等价的EVAL命令并将EVALSHA命令指定的SHAI校验和添加到repl_scriptcache_dict字典里面。
总结
Redis服务器在启动时会对内嵌的Lua环境执行一系列修改操作从而确保内嵌的Lua环境可以满足Redis在功能性、安全性等方面的需要。Redis服务器专门使用一个伪客户端来执行Lua脚本中包含的Redis命令Redis 使用脚本字典来保存所有被EVAL命令执行过或者被SCRIPT LOAD命令载入过的Lua脚本这些脚本可以用于实现SCRIPT EXISTS命令以及实现脚本复制功能。EVL命令为客户端输人的脚本在Lua环境中定义一个函数并通过调用这个函数来执行脚本。EVALSHA命令通过直接调用Lua环境中已定义的函数来执行脚本。SCRIPTFLUSH命令会清空服务器lua scripts字典中保存的脚本并重置Lua环境。SCRIPT EXISTS命令接受一个或多个SHA1校验和为参数并通过检查lua_scripts字典来确认校验和对应的脚本是否存在。SCRIPT LOAD命令接受一个Lua脚本为参数为该脚本在Lua环境中创建函数并将脚本保存到lua_scripts字典中。服务器在执行脚本之前会为Lua环境设置一个超时处理钩子当脚本出现超时运行情况时客户端可以通过向服务器发送SCRIPT KLLL命令来让钩子停止正在执行的脚本或者发送SHUTDOWN nosave命令来让钩子关闭整个服务器。主服务器复制EVAL、SCRIPTFLUSH、SCRIPTLOAD三个命令的方法和复制普通Redis命令一样只要将相同的命令传播给从服务器就可以了。主服务器在复制EVALSHA命令时必须确保所有从服务器都已经载人了EVALSHA命令指定的SHAI校验和所对应的Lua脚本如果不能确保这一点的话主服务器会将EVALSHA命令转换成等效的EVAL命令并通过传播EVAL命令来获得相同的脚本执行效果