百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

详细介绍一下Redis内部是如何执行Lua脚本的?

liuian 2025-03-20 16:22 27 浏览

在之前的分享介绍中,我们知道,Redis是通过EVAL命令或者是通过EVALSHA命令来调用执行Lua脚本的,当客户端请求执行Lua脚本的时候,脚本会通过EVAL命令传递到Redis服务器中,而EVALSHA命令则是通过已经执行了脚本加载命令SCRIPT LOAD加载到Redis中的Lua脚本的SHA1哈希值来调用执行脚本。

在Redis接收到了Lua脚本之后,会将其传递到Lua虚拟机来进行脚本的加载和编译,在Redis内部主要是通过LuaJIT来执行脚本,LuaJIT是一个高效的Lua脚本即时编译器实现,并且专门是针对Lua脚本进行了优化,可以将Lua脚本编译成高度化的机器编码,这种方式可以有效的提高脚本执行的效率。

LuaJIT的工作原理

在Lua脚本解释器执行Lua脚本代码的时候,首先会将Lua脚本源码编译成为字节码文件,然后通过Lua虚拟机去逐条的解释并且执行这些代码中的字节码,而LuaJIT中执行Lua脚本的时候,也会将Lua脚本先转换成字节码,然后会对这个字节码进行进一步的优化来脚本执行的效率。

只不过与标准Lua脚本解释器不同的是LuaJIT提供的事一种即时编译的机制,也就在运行的时候会将字节码即时编译成机器码,这种编译与传统的编译型语言不同的是这种编译是在程序执行的过程中进行的,而传统的C或者C++就是在编译时就已经生成了字节码文件。通过LuaJIT的即时编译机制使得在Lua脚本在脚本执行的时候达到接近原生代码执行的速度。

在脚本执行过程中,LuaJIT技术会自动检测程序中的热路径,所谓的热路径就是指被频繁执行的代码,然后将这些热路径编译成机器码,这样程序中就会直接执行这些已经编译好的机器码,如果某些逻辑是第一次执行,那么在执行的时候也会被JIT机制编译为机器码。在LuaJIT运行的过程中会对数据类型也进行优化,避免在执行机器码的过程中每次都对数据类型进行检查。

在标准的Lua虚拟机中,会通过栈来存储中间运行结果信息,但是在LuaJIT中则是通过寄存器虚拟机来实现这种机制,其实提到寄存器很多开发者就会明白它的操作效率要比栈的操作效率要更高,这样也是通过这种寄存器模型来提高命令执行的效率,减少栈操作带来的其他系统开销。

在LuaJIT中通过使用内联缓存技术来提高内部函数调用以及查找调用的效率,当LuaJIT发现某个函数或者是方法调用频率较高的时候,那么就会对其地址进行缓存,这样这个方法在后续的调用过程中会直接进行跳转而不需要再次进行寻址查找,从而减少了方法查找和调用带来的系统开销。

既然是虚拟机那么不可或缺的就是垃圾回收机制,在LuaJIT中提供的垃圾回收机制与Lua中提供的垃圾回收机制类似,都是采用了标记清除算法,并且对垃圾的回收做了一定的优化,由于Lua脚本的调用执行效率较高,所以垃圾回收的速度效率也会比较高一点。

Lua脚本的键锁定

在之前的介绍中,我们提到过Lua脚本执行的过程中,Redis会对Lua脚本操作的键值进行加锁,这样是为了保证脚本执行的原子性,也就是说在Lua脚本执行的过程中,其他的客户端无法对操作的键值进行修改。

Redis对键值锁定的方式被称为是键级锁定,这种锁定机制通过一种被叫做分布式锁的机制来实现,确保在操作该键的时候不会被其他的客户端操作所修改。也就是说Redis中将会对所有的涉及到操作的键值进行独立管理,在脚本执行的过程中确保脚本的操作不会被其他的命令操作所打断。

从底层实现来讲其实还是基于了Redis的事务队列实现,在脚本执行的过程中,是按照顺序对脚本命令进行执行,如下所示。

int evalGenericCommand(redisClient *c, int issha) {
    // 获取 Lua 脚本中的键
    unsigned long numkeys = 0;
    robj **keys = luaScriptKeys(c->argv[1], c->argc, &numkeys);  // 获取涉及的键

    // 锁定涉及的键
    lockKeysForScript(keys, numkeys);
    
    // 执行 Lua 脚本
    executeLuaScript(c, keys, numkeys);
    
    // 解锁所有键
    unlockKeys(keys, numkeys);
}

当脚本执行完成之后,也会主动去释放所有被锁定的键值。

Redis中如何执行Lua脚本

在Redis中通过EVAL命令来调用Lua脚本,而在编写Lua脚本的时候,涉及到了redis.call()redis.pcall()这两个函数,这两个函数在脚本执行的过程中都会通过Redis提供的处理机制来与Redis数据库底层进行交互,而我们知道Redis底层命令执行是通过一个统一的命令调度框架来实现,也就是说redis.call()redis.pcall()这两个函数也是基于这个Redis命令调度框架来执行。

redis.call()函数的底层实现

在Redis中解析到了redis.call()函数的时候,这条命令就会被解析成Redis支持的RESP格式,也就是说函数调用会被解析为一条Redis的执行命令,然后进入到到Redis的命令调度机制中,通过命令表server.command_table来查找到对应的命令执行函数进行调用,例如常见的SET命令会被映射到setCommand 函数中。

一旦找到了命令处理函数,那么就会执行该函数进行实际的数据处理操作,并修改Redis内部的数据处理状态,处理完成之后返回对应的命令执行结果。

/* 用于执行 Redis 命令 */
int redisCommand(lua_State *lua) {
    int argc = lua_gettop(lua);   // 获取参数的数量
    // 获取命令名称
    const char *command = lua_tostring(lua, 1);
    // 创建一个 Redis 命令的参数列表
    robj **argv = (robj **) zmalloc(sizeof(robj*) * argc);
    for (int i = 0; i < argc; i++) {
        argv[i] = luaToObject(lua, i + 2);  // 从 Lua 栈中获取参数并转换成 Redis 对象
    }
    
    // 执行 Redis 命令
    int retval = call(command, argc, argv);
    zfree(argv);  // 释放参数列表
    return retval;
}

如果在处理过程中,发现了异常redis.call()函数也会抛出异常并且终止Lua脚本的执行,并且会将错误信息返回到客户端。

由于Redis的命令是在Redis主线程中执行完成的,所以在发现错误之后会立即返回并且剩余的命令将不会被执行,这个机制与Redis事务提供的机制有所不同。

redis.pcall()函数的底层实现

这个函数的底层实现与redis.call()的底层实现机制类似,唯一不同的是redis.pcall() 命令提供了一个内置的错误捕获机制,在命令执行的过程中通过这个错误捕获机制来保护Lua脚本执行的准确性,这种机制会捕获在执行过程中发生的任何错误并且会返回一个包含了所有错误的信息表,并不会直接抛出异常。

/* 用于执行 Redis 命令,并捕获错误 */
int redisPcall(lua_State *lua) {
    int argc = lua_gettop(lua);
    const char *command = lua_tostring(lua, 1);
    robj **argv = (robj **) zmalloc(sizeof(robj*) * argc);
    for (int i = 0; i < argc; i++) {
        argv[i] = luaToObject(lua, i + 2);
    }

    // 使用 pcall 保护性调用 Redis 命令
    int retval = lua_pcall(lua, command, argc, argv);
    zfree(argv);
    return retval;
}

也就是说,脚本执行过程中如果某些脚本执行失败了,redis.pcall() 会封装一个Lua的错误异常表,后续的的命令还会继续执行,并不会受到错误异常处理的影响,当脚本执行完成之后,会返回给最终脚本执行之后的一个错误异常表。这种机制就与Redis提供的事务机制有点类似了。

result = redis.pcall('GET', 'non_existing_key')
if result.err then
    -- 错误处理
    return "Key not found"
else
    return result
end

上面两种机制,主要的差异就体现在对于错误的处理方式上,在实际操作过程中我们可以根据实际需要的业务处理方式来选择合适的调用方式。

总结

Redis中执行Lua脚本的过程包括脚本的加载、编译、键的锁定、执行以及结果返回等过程。通过Lua脚本的调用执行,Redis实现了对多个Redis命令组合的的原子操作,并通过锁定机制确保了脚本执行期间的数据一致性和事务性。Lua脚本的原子性和高效性,使得在复杂的操作场景下,Redis 能够提供更加灵活的解决方案。

相关推荐

搭建一个20人的办公网络(适用于20多人的小型办公网络环境)

楼主有5台机上网,则需要一个8口路由器,组网方法如下:设备:1、8口路由器一台,其中8口为LAN(局域网)端口,一个WAN(广域网)端口,价格100--400元2、网线N米,这个你自己会看了:)...

笔记本电脑各种参数介绍(笔记本电脑各项参数新手普及知识)

1、CPU:这个主要取决于频率和二级缓存,频率越高、二级缓存越大,速度越快,现在的CPU有三级缓存、四级缓存等,都影响相应速度。2、内存:内存的存取速度取决于接口、颗粒数量多少与储存大小,一般来说,内...

汉字上面带拼音输入法下载(字上面带拼音的输入法是哪个)

使用手机上的拼音输入法打成汉字的方法如下:1.打开手机上的拼音输入法,在输入框中输入汉字的拼音,例如“nihao”。2.根据输入法提示的候选词,选择正确的汉字。例如,如果输入“nihao”,输...

xpsp3安装版系统下载(windowsxpsp3安装教程)

xpsp3纯净版在采用微软封装部署技术的基础上,结合作者的实际工作经验,融合了许多实用的功能。它通过一键分区、一键装系统、自动装驱动、一键设定分辨率,一键填IP,一键Ghost备份(恢复)等一系列...

没有备份的手机数据怎么恢复

手机没有备份恢复数据方法如下1、使用数据线将手机与电脑连接好,在“我的电脑”中可以看到手机的盘符。  2、将手机开启USB调试模式。在手机设置中找到开发者选项,然后点击“开启USB调试模式”。  3、...

电脑怎么激活windows11专业版

win11专业版激活方法有多种,以下提供两种常用的激活方式:方法一:使用激活密钥激活。在win11桌面上右键点击“此电脑”,选择“属性”选项。进入属性页面后,点击“更改产品密钥或升级windows”。...

华为手机助手下载官网(华为手机助手app下载专区)

华为手机助手策略调整,已不支持从应用市场下载手机助手,目前华为手机助手是需要在电脑上下载或更新手机助手到最新版本,https://consumer.huawei.com/cn/support/his...

光纤线断了怎么接(宽带光纤线断了怎么接)

宽带光纤线断了可以重接,具体操作方法如下:1、光纤连接的时候要根据束管内,同色相连,同芯相连,按顺序进行连接,由大到小。一般有三种连接方法,分别是熔接、活动连接和机械连接。2、连接的时候要开剥光缆,抛...

深度操作系统安装教程(深度操作系统安装教程图解)
  • 深度操作系统安装教程(深度操作系统安装教程图解)
  • 深度操作系统安装教程(深度操作系统安装教程图解)
  • 深度操作系统安装教程(深度操作系统安装教程图解)
  • 深度操作系统安装教程(深度操作系统安装教程图解)
win7旗舰版和专业版区别(win7旗舰版跟专业版)

1、功能区别:Win7旗舰版比专业版多了三个功能,分别是Bitlocker、BitlockerToGo和多语言界面; 2、用途区别:旗舰版的功能是所有版本中最全最强大的,占用的系统资源,...

万能连接钥匙(万能wifi连接钥匙下载)

1、首先打开wifi万能钥匙软件,若手机没有开启WLAN,就根据软件提示打开WLAN开关;2、打开WLAN开关后,会显示附近的WiFi,如果知道密码,可点击相应WiFi后点击‘输入密码’连接;3、若不...

雨林木风音乐叫什么(雨林木风是啥)

雨林木风的创始人是陈年鑫先生。陈年鑫先生于1999年创立了雨林木风公司,其初衷是为满足中国市场对高品质、高性能电脑的需求。在陈年鑫先生的领导下,雨林木风以技术创新、产品质量和客户服务为核心价值,不断推...

aics6序列号永久序列号(aics6破解序列号)

关于AICS6这个版本,虽然是比较久远的版本,但是在功能上也是十分全面和强大的,作为一名平面设计师的话,AICS6的现有的功能已经能够应付几乎所有的设计工作了……到底AICC2019的功能是不是...

win7正在启动windows 卡住(win7正在启动windows卡住了 进入安全模式)
  • win7正在启动windows 卡住(win7正在启动windows卡住了 进入安全模式)
  • win7正在启动windows 卡住(win7正在启动windows卡住了 进入安全模式)
  • win7正在启动windows 卡住(win7正在启动windows卡住了 进入安全模式)
  • win7正在启动windows 卡住(win7正在启动windows卡住了 进入安全模式)
手机可以装电脑系统吗(手机可以装电脑系统吗怎么装)

答题公式1:手机可以通过数据线或无线连接的方式给电脑装系统。手机安装系统需要一定的技巧和软件支持,一般需要通过数据线或无线连接的方式与电脑连接,并下载相应的软件和系统文件进行安装。对于大部分手机用户来...