欲求不满之 Redis Lua 脚本的执行原理

【OpenCV】手眼标定实战:从原理到calibrateHandEye的完整实现 本文详细解析了机器人视觉中的手眼标定原理与实战,重点介绍了如何使用OpenCV的calibrateHandEye函数实现从数据采集到结果验证的完整流程。文章深入剖析了AX=XB核心方程,区分了眼在手上与眼在手外两种配置,并提供了详细的代码示例与避坑指南,帮助开发者精准建立相机与机械臂末端的坐标转换关系,是机器人精准抓取的关键技术。 阅读详情

Redis 提供了非常丰富的指令集,但是用户依然不满足,希望可以自定义扩充若干指令来完成一些特定领域的问题。Redis 为这样的用户场景提供了 lua 脚本支持,用户可以向服务器发送 lua 脚本来执行自定义动作,获取脚本的响应数据。Redis 服务器会单线程原子性执行 lua 脚本,保证 lua 脚本在处理的过程中不会被任意其它请求打断。

640?wx_fmt=png
图片

比如在《Redis 深度历险》分布式锁小节,我们提到了 del_if_equals 伪指令,它可以将匹配 key 和删除 key 合并在一起原子性执行,Redis 原生没有提供这样功能的指令,它可以使用 lua 脚本来完成。

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

127.0.0.1:6379> set foo bar
OK
127.0.0.1:6379> eval 'if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end' 1 foo bar
(integer) 1
127.0.0.1:6379> eval 'if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end' 1 foo bar
(integer) 0

EVAL SCRIPT KEY_NUM KEY1 KEY2 ... KEYN ARG1 ARG2 ....

SCRIPT LOAD 和 EVALSHA 指令

在上面的例子中,脚本的内容很短。如果脚本的内容很长,而且客户端需要频繁执行,那么每次都需要传递冗长的脚本内容势必比较浪费网络流量。所以 Redis 还提供了 SCRIPT LOAD 和 EVALSHA 指令来解决这个问题。

640?wx_fmt=png
图片

SCRIPT LOAD 指令用于将客户端提供的 lua 脚本传递到服务器而不执行,但是会得到脚本的唯一 ID,这个唯一 ID 是用来唯一标识服务器缓存的这段 lua 脚本,它是由 Redis 使用 sha1 算法揉捏脚本内容而得到的一个很长的字符串。有了这个唯一 ID,后面客户端就可以通过 EVALSHA 指令反复执行这个脚本了。我们知道 Redis 有 incrby 指令可以完成整数的自增操作,但是没有提供自乘这样的指令。

incrby key value  ==> $key = $key + value
mulby key value ==> $key = $key * value

local curVal = redis.call("get", KEYS[1])
if curVal == false then
  curVal = 0
else
  curVal = tonumber(curVal)
end
curVal = curVal * tonumber(ARGV[1])
redis.call("set", KEYS[1], curVal)
return curVal

local curVal = redis.call("get", KEYS[1]); if curVal == false then curVal = 0 else curVal = tonumber(curVal) end; curVal = curVal * tonumber(ARGV[1]); redis.call("set", KEYS[1], curVal); return curVal

127.0.0.1:6379> script load 'local curVal = redis.call("get", KEYS[1]); if curVal == false then curVal = 0 else curVal = tonumber(curVal) end; curVal = curVal * tonumber(ARGV[1]); redis.call("set", KEYS[1], curVal); return curVal'
"be4f93d8a5379e5e5b768a74e77c8a4eb0434441"

127.0.0.1:6379> evalsha be4f93d8a5379e5e5b768a74e77c8a4eb0434441 1 notexistskey 5
(integer) 0
127.0.0.1:6379> evalsha be4f93d8a5379e5e5b768a74e77c8a4eb0434441 1 notexistskey 5
(integer) 0
127.0.0.1:6379> set foo 1
OK
127.0.0.1:6379> evalsha be4f93d8a5379e5e5b768a74e77c8a4eb0434441 1 foo 5
(integer) 5
127.0.0.1:6379> evalsha be4f93d8a5379e5e5b768a74e77c8a4eb0434441 1 foo 5
(integer) 25

错误处理

上面的脚本参数要求传入的附加参数必须是整数,如果没有传递整数会怎样呢?

127.0.0.1:6379> evalsha be4f93d8a5379e5e5b768a74e77c8a4eb0434441 1 foo bar
(error) ERR Error running script (call to f_be4f93d8a5379e5e5b768a74e77c8a4eb0434441): @user_script:1: user_script:1: attempt to perform arithmetic on a nil value

640?wx_fmt=png
图片

如果读者对 lua 语言有所了解就知道 lua 原生没有提供 try catch 语句,那上面提到的异常包裹语句究竟是用什么来实现的呢?lua 的替代方案是内置了 pcall(f) 函数调用。pcall 的意思是 protected call,它会让 f 函数运行在保护模式下,f 如果出现了错误,pcall 调用会返回 false 和错误信息。而普通的 call(f) 调用在遇到错误时只会向上抛出异常。在 Redis 的源码中可以看到 lua 脚本的执行被包裹在 pcall 函数调用中。

// 编译期
int luaCreateFunction(client *c, lua_State *lua, char *funcname, robj *body) {
  ...
  if (lua_pcall(lua,0,0,0)) {
    addReplyErrorFormat(c,"Error running script (new function): %s\n",
            lua_tostring(lua,-1));
    lua_pop(lua,1);
    return C_ERR;
  }
  ...
}

// 运行期
void evalGenericCommand(client *c, int evalsha) {
  ...
  err = lua_pcall(lua,0,1,-2);
  ...
}

错误传递

redis.call 函数调用会产生错误,脚本遇到这种错误会返回怎样的信息呢?我们再看个例子

127.0.0.1:6379> hset foo x 1 y 2
(integer) 2
127.0.0.1:6379> eval 'return redis.call("incr", "foo")' 0
(error) ERR Error running script (call to f_8727c9c34a61783916ca488b366c475cb3a446cc): @user_script:1: WRONGTYPE Operation against a key holding the wrong kind of value

127.0.0.1:6379> eval 'return redis.pcall("incr", "foo")' 0
(error) WRONGTYPE Operation against a key holding the wrong kind of value

脚本死循环怎么办?

Redis 的指令执行是个单线程,这个单线程还要执行来自客户端的 lua 脚本。如果 lua 脚本中来一个死循环,是不是 Redis 就完蛋了?Redis 为了解决这个问题,它提供了 script kill 指令用于动态杀死一个执行时间超时的 lua 脚本。不过 script kill 的执行有一个重要的前提,那就是当前正在执行的脚本没有对 Redis 的内部数据状态进行修改,因为 Redis 不允许 script kill 破坏脚本执行的原子性。比如脚本内部使用了 redis.call("set", key, value) 修改了内部的数据,那么 script kill 执行时服务器会返回错误。下面我们来尝试以下 script kill 指令。

127.0.0.1:6379> eval 'while(true) do print("hello") end' 0

127.0.0.1:6379> script kill
OK
(2.58s)

127.0.0.1:6379> eval 'while(true) do print("hello") end' 0
(error) ERR Error running script (call to f_d395649372f578b1a0d3a1dc1b2389717cadf403): @user_script:1: Script killed by user with SCRIPT KILL...
(6.99s)

Script Kill 的原理

下面我就要开始揭秘 kill 的原理了,lua 脚本引擎功能太强大了,它提供了各式各样的钩子函数,它允许在内部虚拟机执行指令时运行钩子代码。比如每执行 N 条指令执行一次某个钩子函数,Redis 正是使用了这个钩子函数。

640?wx_fmt=png
图片
void evalGenericCommand(client *c, int evalsha) {
  ...
  // lua引擎每执行10w条指令,执行一次钩子函数 luaMaskCountHook
  lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
  ...
}

思考题

在延时队列小节,我们使用 zrangebyscore 和 zdel 两条指令来争抢延时队列中的任务,通过 zdel 的返回值来决定是哪个客户端抢到了任务,这意味着那些没有抢到任务的客户端会有这样一种感受 —— 到了嘴边的肉(任务)最后还被别人抢走了,会很不爽。如果可以使用 lua 脚本来实现争抢逻辑,将 zrangebyscore 和 zdel 指令原子性执行就不会存在这种问题,读者可以尝试一下。

注:如果读者不熟悉 lua,建议先学习 lua 语言,lua 语言简单易学,但是也不是几分钟就可以学会的事,需要再来一本小册的内容。本小册专注 Redis,所以就不开大篇内容来细讲 lua 语言了,有需要的朋友可以搜索相关在线教程。

640?wx_fmt=jpeg

阅读更多深度技术文章,扫一扫上面的二维码关注微信公众号「码洞」

GD32H7实战:5步搞定YOLO轻量化模型部署到MCU(附避坑指南) 本文详细介绍了如何将YOLO轻量化模型部署到GD32H7 MCU的完整实战流程。通过五个关键步骤,涵盖环境搭建、模型训练、格式转换到工程集成,并重点分享了使用GD32AI-ModelZoo工具链进行MCU部署的避坑指南,帮助开发者在资源受限的边缘设备上实现高效目标检测。 阅读详情

相关推荐

无耳科技 Solon v3.0.8 发布,Java 企业级应用开发框架

Solon 是新一代,Java 企业级应用开发框架。是杭州无耳科技有限公司的“根级”开源项目(最近“杭州六小龙”很火啊,我们也是杭州的哦)。从零开始构建(No Spring、No Java-EE、No Servlet),有灵活的接口规范与开放生态。采用对商用友好的 Apache 2.0 开源协议。

向往自由的少年!(我是一个少年!!!) 1700

redis 基本命令

基本命令 # 客户端连接服务器 redis-cli -h <address> -p <port> -a <password> # 列出服务器信息 INFO # 查看服务是否允许 PING key # 判断键的存在; 返回存在的键的数量 EXISTS <key1> <key2> # 删除键 DEL <key1> <ke............

firefly 2745

统信UOS1070批量整机备份安装标准化方案

Linux整机备份是政企级操作系统交付的核心能力,其本质是将系统状态、硬件适配、安全策略与部署流程在启动阶段原子化固化。基于内核overlayfs与initrd可编程机制,Ventoy实现了ISO镜像与定制配置的解耦部署,显著提升UOS专业版1070在多机型环境下的兼容性、可审计性与复现一致性。该方案规避传统克隆工具的镜像膨胀、硬件绑定和策略注入失效等缺陷,适用于无网、高合规、强管控场景,广泛支撑政务终端、教育机房及金融网点的规模化交付。

weixin_33892359的博客 404

redis脚本命令执行(redis.call)

1、redis-cli命令行中执行: # 调用redis命令设置缓存 # 不传参数 eval "return redis.call('set', 'name1', 'Tom')" 0 # 传入1个值参数 eval "return redis.call('set', 'name2', ARGV[1])" 0 "Tom" # 传入1个键名参数和1个值参数 eval "return redis.call('set', KEYS[1], ARGV[1])" 1 "name3" "Tom" 2、linux命

落白 1万+

欧姆龙PLC与fanuc发那科机器人进行Ethrenet ip通信测试

近期在做发那科FANUC的通讯,会吧CC-LINK、Profibus-DP、Profinet I/O、DeviceNet、EtherNet/IP这些常用通讯做成文档发上来,大家喜欢的话可以关注 注:做对应的通讯,在机器人控制柜都需要安装对应的板卡以及软件功能 截图中的程序设置和机器人EDS插件在下面链接 https://download.youkuaiyun.com/download/gojava/13770799 PLC为主站 机器人为从站 一、硬件部分 欧姆龙PLC ...

七兔自习室 1万+

用VINS-Fusion跑通EuRoC数据集:Ubuntu22.04+ROS2环境下的保姆级评测指南

本文详细介绍了在Ubuntu22.04和ROS2 Humble环境下使用VINS-Fusion处理EuRoC数据集的完整流程。从环境配置、数据集转换到参数调优和性能评估,提供了一套全面的多传感器融合分析方案,帮助开发者在无人机精准定位场景中实现高效部署与优化。

weixin_29212867的博客 518

redis 精髓

一、初探 1、简介 Redis是一个由ANSI C语言编写,性能优秀、支持网络、可持久化的内存数据库,并提供多种语言的API,线程安全,常用于做缓存和分布式锁,支持事务、持久化、主从复制、发布订阅、LUA脚本、LRU驱动事件、流水线、多种集群方案。 Redis跟memcached的只支持k-v类型不同,redis常用的类型主要是 String、List、Hash、Set、ZSet 这5种,并...

真是呆子啊的博客 1432

万达电影 com.wandafilm.app headers check

万达电影 安卓逆向 抓包 逆向 check

weixin_43786514的博客 1438

实战篇Redis(2)

分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路 那么分布式锁他应该满足一些什么样的条件呢?可见性:多个线程都能看到相同的结果,注意:这个地方说的可见性并不是并发编程中指的内存可见性,只是说多个进程之间都能感知到变化的意思互斥:互斥是分布式锁的最基本的条件,使得程序串行执行高可用:程序不易崩溃,时时刻刻都保证较高的可用性高性能:由于加锁本身就让性能降低

wuzj1314的博客 948

【06】新国都——新国都 嵌入式笔试,校招,题目记录及解析

本文总结了新国都嵌入式校招笔试题目及解析,内容涵盖C语言基础、Linux系统、数据结构与算法等多个方面。主要包括:1)不定项选择题10道,涉及字符串常量、蓝牙与GPRS区别、编译错误识别等;2)判断题10道,考察Linux权限、循环控制、内存管理等;3)填空题5道,包括编码计算、逻辑运算、宏定义等;4)简答题2道,解析全局变量引用和进程线程区别。题目难度适中,重点考察嵌入式开发基础知识和编程能力,并提供详细解析和扩展知识链接,对准备嵌入式相关笔试具有参考价值。

程序员,他们想的是什么?他们想的永远都是技术,他们崇尚的也永远都是技术。 1435

基于激光雷达与视觉融合的四旋翼无人机室内避障方法研究

本文研究了一种基于激光雷达与视觉融合的四旋翼无人机室内避障方法。针对单一传感器在室内环境中的局限性,提出通过激光雷达提供精确几何距离、视觉相机补充语义信息的融合方案。研究内容包括:搭建ROS硬件平台集成单目相机与2D激光雷达;设计基于扩展卡尔曼滤波的传感器融合模型;改进VFH算法结合视觉深度图进行路径规划。实验验证表明,该方法在复杂室内环境下具有较高的鲁棒性、实时性与避障成功率,为解决无GPS信号环境下的无人机自主导航问题提供了有效方案。

huanghm88的专栏 348

随手记录4:spring上下文ApplicationContext 两种获取方式和使用获取普通对象和FactoryBean的不同;

随手记录4:spring上下文ApplicationContext 获取和使用;简介关于ApplicationContext 获取关于ApplicationContext 使用1.UserService .java2.FactoryBeanConfig .java结论 简介 项目中基本上好多对象都是交给spring管理的; 但是有些静态的工具类中想获取该对象又不能通过 @Autowired直接注入; 或者有些方法里面需要使用到spring托管的对象; 可以通过ApplicationContext 直接获

weixin_43812522的博客 745

优化翼型形状的遗传算法实现

在每一代种群中,通过计算每个个体的适应度值,选择具有较高适应度值的个体,并使用交叉和变异算子对它们进行操作,从而生成新的个体。具体而言,我们将采用一个参数化的翼型模型,其中包括几何设计变量,如角度、弯曲和厚度等,以及性能指标,如升阻比和扭矩等。总之,本文提供了一个基于遗传算法的优化方法,用于寻找最优的翼型形状,并提供MATLAB源代码。需要注意的是,上述代码中的objFunc函数是一个示例性质的目标函数,实际应用中需要根据具体的问题进行修改。1.初始化一个随机的种群,其中每个个体代表一个参数化的翼型模型。

与其临渊羡鱼,不如退而结网 518

Redis基础面试题:Redis中的Lua脚本执行原理与原子性保证机制

面试官通过今天的讨论,我们深入了解了Redis中Lua脚本的执行原理、原子性保证机制,以及相关的优化与限制。你还有什么补充吗?Victor:补充一点,Lua脚本是Redis中非常强大的功能,但也需要谨慎使用。在实际开发中,应根据业务需求权衡脚本的复杂度与性能,确保既能满足功能需求,又不会对Redis服务造成负面影响。面试官:完全同意。

qq_61302385的博客 4186

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

【Redis】Redis中使用Lua脚本

Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua具体语法参考:https://www.runoob.com/lua/lua-tutorial.html。

morris 3万+

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

【进阶篇】2.4 Redis Lua脚本详解

Redis 其内置了脚本语言Lua,允许用户通过Lua脚本执行一系列操作。本篇博客将深入探讨Redis Lua脚本的执行机制,包括加载脚本、编译脚本和执行脚本的过程,以及脚本的原子性和事务性。我们将通过实例演示的方式,展示如何在Redis中使用Lua脚本,以及Lua脚本在Redis中的应用场景。让我们一起来了解Redis Lua脚本的强大之处吧!Lua是一种轻量级的脚本语言,它具有简洁、灵活、高效的特点。Lua语言最初是为嵌入式系统设计的,但现在已广泛应用于游戏开发、Web开发、嵌入式设备控制等领域。

日拱一卒无有尽,功不唐捐终入海 1万+

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

【实践篇】4.6 Redis Lua脚本执行原理和语法示例

在学习本文之前,我是强烈建议大家了解一下我的其他两篇博客《Redis 从入门到精通【进阶篇】一文学会Lua脚本》《Redis 从入门到精通【进阶篇】之Lua脚本详解》Redis通过嵌入Lua解释器,实现了对Lua脚本的执行。在执行过程中,Redis保证了脚本的原子性和阻塞性,同时通过脚本缓存和访问限制来提高性能和安全性。这使得Lua脚本成为在Redis中实现复杂逻辑的有效手段嵌入式Lua解释器:Redis内部集成了Lua解释器,这使得Redis可以直接执行Lua脚本。

日拱一卒无有尽,功不唐捐终入海 3994

redis之lua实现原理

redis之lua实现原理

SO_zxn的博客 1383

Redis Lua脚本好处、Redis执行Lua的两种方式、Redis缓存Lua脚本、Redis Lua原子性验证、Lua脚本IP限流、Lua脚本自乘

以客户端执行乘法脚本为例,在redis客户端执行Lua语言,拼接了很多命令,如果这样的一长执行串命令发给服务端,那么网络通信就会产生比较大的开销,所以Redis支持在服务端直接缓存一部分脚本的内容(它是用脚本生成了一段摘要,服务端可以根据这个摘要去执行脚本内容)。Lua脚本实现原子性的原理,就是执行一个Lua脚本的时候,里面有多个命令,这个时候会阻塞其它任何命令的执行和其它任何客户端的请求,也就是说,执行Lua的时候,只会执行Lua里的命令,以此来达到Lua里的多条命令成为一个整体来执行。

郝开的博客 2694

【JAVA高级】如何使用Redis加锁和解锁(一)、Lua脚本执行原理及流程

在Redis中加锁和解锁通常是通过Redis的原子性命令来实现的,以保证操作的原子性和线程安全。

qq_39666711的博客 2089

Redis系列——Lua脚本和redis事务的应用

本文主要阐述了Redis中Lua脚本与事务机制的功能,并深入分析了它们的优势与局限性。

庄的博客 1917

Redis篇-5--原理篇4--Lua脚本

Redis 支持使用 Lua 脚本来执行复杂的操作,这为 Redis 提供了更强的灵活性和性能优化能力。通过 Lua 脚本,你可以在服务器端执行一系列命令,而不需要多次往返客户端与服务器之间,从而减少了网络延迟并提高了效率。此外,Lua 脚本是原子性的,这意味着在脚本执行期间,其他客户端的请求不会被处理,确保了数据的一致性。

weisian的博客 3万+

Spring Boot中使用Redis和Lua脚本实现延时队列

通过使用Redis和Lua脚本,我们可以在Spring Boot环境中实现一个高效且可靠的延时队列系统。这种方法利用了Redis的有序集合数据结构和Lua脚本的原子性操作来确保任务的正确性和一致性。通过定期调度任务来处理到期的任务,我们可以实现各种需要延迟执行的操作,如发送提醒、执行定时任务等。

码到三十五 2万+

Redis 3.0 Lua脚本终极指南:从源码解析原子性执行原理

Redis 3.0 引入了强大的 Lua 脚本功能,彻底改变了我们使用 Redis 的方式。通过 EVAL 和 EVALSHA 命令,开发者可以在 Redis 服务器端执行复杂的多命令操作,同时保证**原子性执行**和**高性能**。本文将深入剖析带有详细注释的 Redis 3.0 源码,揭示 Lua 脚本在 Redis 中的工作机制。 ## 🔍 什么是 Redis Lua 脚本? Redi

gitblog_00454的博客 853
上一篇: 自己动手实现 Shell多进程套套符
下一篇: 短小精悍之 Redis 命令行工具有趣的罕见用法
codehole_
博客等级 码龄7年 34粉丝 132原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值