【Redis】Lua脚本在Redis中的基本使用及其原子性保证原理

文章目录

    • 背景
    • 一、Eval
    • 二、EvalSHA
    • 三、Redis 对 Lua 脚本的管理
      • 3.1 script flush
      • 3.2 script exists
      • 3.3 script load
      • 3.4 script kill
    • 四、Lua在Redis中原子性执行的原理

背景

Lua 本身是一种轻量小巧的脚本语言,在Redis2.6版本开始引入了对Lua脚本的支持。通过在服务器中嵌入Lua环境,Redis客户端就可以使用Lua脚本,直接在服务器端原子地执行多个Redis命令。在Redis中Lua有两种执行方式:Eval和EvalSHA。

一、Eval

通过Redis内置的 Lua 解释器,可以使用 EVAL 命令(也可以使用redis-cli 的–eval 参数)对 Lua 脚本进行解析。需要注意的点是执行Lua也会使Redis阻塞。
在这里插入图片描述
Eval命令的执行过程主要可以分为三个步骤:

  • 根据客户端给定的Lua脚本,在Lua环境中定义一个Lua函数,Lua函数的名称实际上是f_为前缀加上脚本本身计算出来的SHA1值组成,如f_ddfsdfjgjbg33rndgj00,SHA1的长度是40字符,而函数体则是脚本本身。
  • 将客户端给定的脚本保存到lua_scripts字典,简单来说就是添加一个key-value,key就是Lua脚本的SHA1校验和,而值是Lua脚本本身,这主要是用于以后使用。
  • 执行第一步在Lua环境中定义的函数,以此来执行客户端中给定的Lua脚本。

在Redis中,使用了Key列表和参数列表来为Lua脚本提供更多的灵活性,执行Eval命令的格式为:

eval  脚本内容 key个数 key列表 参数列表

栗子1:

eval "return 'hello lua'" 0

栗子2:

127.0.0.1:6379> eval 'return "hello " .. KEYS[1] .. ARGV[1]' 1 redis world执行返回:"hello world"

二、EvalSHA

EvalSHA 中方式的是拆分成两个步骤,首先要将Lua脚本加载到Redis服务端,得到该脚本的SHA1校验码。然后使用EvalSHA命令使用SHA1作为参数可以直接执行对应Lua脚本。这样做的好处是可以避免每次发送Lua脚本的开销,而脚本也会常驻在服务端,脚本功能得到了复用。缺点是要怎么管理这些脚本和命令过多的话会占用Redis的内存。
在这里插入图片描述
在介绍Eval命令执行过程中,第一步会在Lua环境中生成一个Lua脚本对应的函数,形如:f_dfdugndgub320433,只要脚本对应的函数在Lua中定义过,那么即使不知道脚本的内容本身,客户端也是可以根据脚本的SHA1来调用脚本对应的函数,从而达到执行脚本的目的,这也就是EvalSHA命令的实现原理。

EvalSHA命令的一般格式:

evalsha sha1值 key个数 key列表 参数列表

如:

eval "return 'hello lua'" 0
// 执行完eval后,Lua环境就定义了一个函数名为:f_dfaujjgdgu388vdf83803(),那么就可以根据对应的sha1值来调用函数了。
evlsha "dfaujjgdgu388vdf83803" 0

三、Redis 对 Lua 脚本的管理

除了Eval和EvalSHA命令之外,Redis中与Lua脚本相关的命令还有:script flushscript existsscript load以及script kill

3.1 script flush

script flush命令用于清除服务器中的所有和Lua脚本相关的信息,这个命令会释放并重建lua_scripts字典,关闭现有的Lua环境并重建一个新的Lua环境。

命令格式:

script flush

栗子:

127.0.0.1:6379> script flush
OK

3.2 script exists

script exists命令会根据输入的SHA1校验和,检查校验和对应的脚本是否存在于服务器中。该命令主要是通过检查给定的SHA1是否存在于lua_scripts字典来实现的。

命令格式:

script exists sha1[sha1…]

返回结果代表 sha1[sha1…]被加载到 Redis 内存的个数。

栗子:

127.0.0.1:6379> script exists 5ea77eda7a16440abe244e6a88fd9df204ecd5aa
1) (integer) 1

3.3 script load

script load命令和Eval命令执行Lua脚本的前两步完全一样,该命令首先在Lua环境中创建相对应的函数,然后再将脚本保存到lua_scripts字典中。

命令格式:

script load lua脚本

栗子:

127.0.0.1:6379> script load "return 'hello lua'"
"5ea77eda7a16440abe244e6a88fd9df204ecd5aa"

3.4 script kill

如果Lua脚本比较耗时,甚至Lua脚本存在问题,那么此时Lua脚本的执行会阻塞Redis,直到脚本执行完毕或 者外部进行干预将其结束,就可以使用script kill来杀掉正在执行的 Lua 脚本。

举个栗子:

## 写一个死循环的lua脚本并在redis客户端执行
127.0.0.1:6379> eval 'while 1==1 do end' 0
## 重新起一个客户端去执行命令,返回了报错信息,此时Redis已经阻塞,无法处理正常的调用,这时可以选择继续等待。
## 但更多时候需要快速将脚本杀掉。使用shutdown save显然不太合适,所以选择script kill,当script kill执行之后,客户端调用会恢复
127.0.0.1:6379> get test
(error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.
## 另起一个客户端,停止脚本执行
127.0.0.1:6379> script kill
OK
127.0.0.1:6379> get k1
"11"

此外,Redis提供了一个lua-time-limit参数,用于配置Lua脚本执行的超时时间,当 Lua 脚本时间超过lua-time-limit后,向其他命令调用发送BUSY的信号,并不会停止掉服务端和客户端的脚本执行,所以当某个Lua脚本执行达到lua-time-limit值之后,其他客户端在执行正常的命令时,将会收到“Busy Redis is busy running a script”错误,并且提示使用script kill或者shutdown nosave命令来杀掉这个 busy 的脚本。

实际上,如果Redis服务端设置了lua-time-limit参数,那么在每次执行Lua脚本之前,服务器都会在Lua环境里面设置一个超时的处理钩子(hook)。超时处理钩子再脚本运行期间,会定期检查脚本已经运行了多长时间,一旦钩子发现脚本的运行时间已经超过了lua-time-limit设定的时长,那么钩子将定期在脚本运行的间隙中,查看是否有script kill或者shutdown nosave到达服务器。如果超时运行的脚本未执行过任何的写入操作,那么客户端可以通过script kill命令停止脚本的运行,并向执行该脚本的客户端发送一个错误回复。处理完script kill命令后服务器可以继续运行。
此外,如果超时脚本已经执行过写入操作,那么客户端只能用shutdown nosave命令来停止服务器,从而防止不合法的数据被写入。

四、Lua在Redis中原子性执行的原理

  在Redis中,Lua脚本能够保证原子性的主要原因还是Redis采用了单线程执行模型。也就是说,当Redis执行Lua脚本时,Redis会把Lua脚本作为一个整体并把它当作一个任务加入到一个队列中,然后单线程按照队列的顺序依次执行这些任务,在执行过程中Lua脚本是不会被其他命令或请求打断,因此可以保证每个任务的执行都是原子性的。

这块儿想要继续了解的可以参考:为什么Redis单线程却能支撑高并发?Redis6.0之后为什么又引入多线程?

在实际开发中,为了提高效率,通常我们能让lua脚本接收多个参数,一次发送到Redis服务端,还能将key进行整理,将相同slot的key放到同一批进行处理,这在性能优化中能减少网络传输的开销,同时也能并发的发送给Redis服务端。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/107297.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

笔记本多拓展出一个屏幕

一、首先要知道,自己的电脑有没有Type-c接口,支持不支持VGA 推荐: 自己不清楚,问客服,勤问。 二、显示屏与笔记本相连,通过VGA 三、连接好了,需要去配置 网址:凑合着看&#xff…

【Unity编辑器扩展】| 顶部菜单栏扩展 MenuItem

前言【Unity编辑器扩展】 | 顶部菜单栏扩展 MenuItem一、创建多级菜单二、创建可使用快捷键的菜单项三、调节菜单显示顺序和可选择性四、创建可被勾选的菜单项五、右键菜单扩展5.1 Hierarchy 右键菜单5.2 Project 右键菜单5.3 Inspector 组件右键菜单六、AddComponentMenu 特性…

如何在多版本C#工程上添加程序集

给出工程配置文件添加程序集示例&#xff1a; <ItemGroup Condition"$(TargetFramework) net451"> <PackageReference Include"MySql.Data" Version"6.7.9" /> <Reference Include"System.Configuration" /&…

如何使用谷歌浏览器连接linux服务器SSH服务

环境&#xff1a; 谷歌浏览器 版本 116.0.5845.141&#xff08;正式版本&#xff09; &#xff08;64 位&#xff09; Win10 专业版 安全外壳 (SSH)v.0.58 问题描述&#xff1a; 如何使用谷歌浏览器连接linux服务器SSH服务 解决方案&#xff1a; 1.找了有台安装好了这个插…

java企业数据管理系统

项目介绍 此项目为企业数据管理系统的后端部分&#xff0c;前端部分请参考vue-admin&#xff0c;项目实现了菜单管理、用户管理、角色管理和权限管理四个基础模块&#xff0c;前端菜单管理结合动态路由可自由添加菜单。结合Shiro权限管理实现了菜单和按钮的权限控制。 ❝ 前端…

C语言实现三子棋游戏(详解)

目录 引言&#xff1a; 1.游戏规则&#xff1a; 2.实现步骤&#xff1a; 2.1实现菜单&#xff1a; 2.2创建棋盘并初始化&#xff1a; 2.3绘制棋盘&#xff1a; 2.4玩家落子&#xff1a; 2.5电脑落子&#xff1a; 2.6判断胜负&#xff1a; 3.源码&#xff1a; 结语&…

XSC63-300-S-CB、XSC80-400-S-CA、XSC100-700-S-LB方型气缸

XQVB40-16、XQ-VB63-16、XQ-VB100-16倍压增压阀 QCK2400、QCK2400A、QCK2422、QCK2422A磁性开关 AL-07R、AL-30R、KT-50R、AL-03R、AL-20R、AL-72R磁性开关 XSC32-40-S-LB、XSC40-100-S-FA、XSC50-200-S-FB、XSC63-300-S-CB、XSC80-400-S-CA、XSC100-700-S-LB方型气缸 QSC3…

IDEA设置Maven 镜像

第一步&#xff1a;右键项目&#xff0c;选择Maven->Create ‘settings.xml’ 已经存在的话是Open ‘settings.xml’&#xff1a; 第二步&#xff1a;在settings.xml文件中增加阿里云镜像地址&#xff0c;代码如下&#xff1a; <?xml version"1.0" encodin…

Trinitycore学习之在vscode查看远端服务器上源码配置

1&#xff1a;安装vscode&#xff0c;去官网下载&#xff0c;这里下载windows版本安装包 .zip https://code.visualstudio.com/Download 2&#xff1a;安装后&#xff0c;安装扩展chinese&#xff0c;使用中文设置&#xff0c;需要重启vscode。 3&#xff1a;安装ssh相关插件…

K8S基础概念

1、Node Node作为集群中的工作节点&#xff0c;运行真正的应用程序&#xff0c;在Node上Kubernetes管理的最小运行单元是Pod。Node上运行着Kubernetes的Kubelet、kube-proxy服务进程&#xff0c;这些服务进程负责Pod的创建、启动、监控、重启、销毁、以及实现软件模式的负载均…

LeetCode(力扣)45. 跳跃游戏 IIPython

LeetCode45. 跳跃游戏 II 题目链接代码 题目链接 https://leetcode.cn/problems/jump-game-ii/description/ 代码 class Solution:def jump(self, nums: List[int]) -> int:if len(nums) 1:return 0curdis 0nextdis 0step 0for i in range(len(nums)):nextdis max(…

[keil] uv编译分析

假设Keil安装路径: C:\Keil_v5\ 假设工程在 d:\HELLO , 工程Targets名:Simulator [在Manage Project Items中可修改] 如下指令为:Build(F7) C:\Keil_v5\UV4\UV4.exe -b d:\HELLO\Hello.uvproj -j0 -t Simulator -o d:\HELLO\uv4.log 如下指令为:Rebuild(CtrlAltF7) C:\Kei…