【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.rhkb.cn/news/133942.html

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

相关文章

Hadoop源码阅读(二):DataNode启动

说明: 1.Hadoop版本:3.1.3 2.阅读工具:IDEA 2023.1.2 3.源码获取:Index of /dist/hadoop/core/hadoop-3.1.3 (apache.org) 4.工程导入:下载源码之后得到 hadoop-3.1.3-src.tar.gz 压缩包,在当前目录打开Pow…

【算法|虚拟头节点|链表】移除链表元素

Leetcode203 移除链表元素 题目描述: 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val val 的节点,并返回 新的头节点 。 示例 1: 输入:head [1,2,6,3,4,5,6], val 6 输出&#xf…

通过curl命令分析http接口请求各阶段的耗时等

目录 一、介绍二、功能1、-v 输出请求 响应头状态码 响应文本等信息2、-x 测试代理ip是否能在该网站使用3、-w 额外输出查看接口请求响应的消耗时间4、-o 将响应结果存储到文件里面5、-X post请求测试 (没测成功用的不多) 一、介绍 Curl是一个用于发送和接收请求的命令行工具和…

使用Python CV2融合人脸到新图片--优化版

优化说明 上一版本人脸跟奥特曼图片合并后边界感很严重,于是查找资料发现CV2还有一个泊松函数很适合融合图像。具体代码如下: import numpy as np import cv2usrFilePath "newpic22.jpg" atmFilePath "atm2.jpg" src cv2.imrea…

基于matlab实现的额 BP神经网络电力系统短期负荷预测未来(对比+误差)完整程序分享

基于matlab实现的额 BP神经网络电力系统短期负荷预测 完整程序: clear; clc; %%输入矢量P(15*10) P[0.2452 0.1466 0.1314 0.2243 0.5523 0.6642 0.7105 0.6981 0.6821 0.6945 0.7549 0.8215 0.2415 0.3027 0; 0.2217 0.1581 0.1408 0.23…

【JVM 内存结构丨堆】

堆 定义内存分配特点:分代结构对象分配过程Full GC /Major GC 触发条件引用方式堆参数堆内存实例 主页传送门:📀 传送 定义 JVM(Java Virtual Machine)堆是Java应用程序运行时内存管理的重要组成部分之一。堆内存用于存储Java对象…

深度思考ES面经

1 推荐文章 2万字详解,吃透 Elasticsearch 2 什么是倒排索引,为什么这么叫? 倒排索引(Inverted Index)是一种为快速全文搜索而设计的数据结构。它被广泛应用于搜索引擎,其中 Elasticsearch(简…

CVPR2023 RIFormer, 无需TokenMixer也能达成SOTA性能的极简ViT架构

编辑 | Happy 首发 | AIWalker 链接 | https://mp.weixin.qq.com/s/l3US8Dsd0yNC19o7B1ZBgw project, paper, code Token Mixer是ViT骨干非常重要的组成成分,它用于对不同空域位置信息进行自适应聚合,但常规的自注意力往往存在高计算复杂度与高延迟问题。…

软文推广在企业中运用的优势有哪些?

随着互联网的发展,越来越多的企业在推广方式上已经逐渐脱离于传统媒体,软文推广已经成为了企业宣传的主要方式。也有不少企业来找盒子进行推广,接下来媒介盒子就来告诉大家,企业进行软文推广的优势有哪些? 成本低 传统…

ELK 企业级日志分析系统 ELFK

目录 一、概述 二、组件介绍 2.1、ElasticSearch 2.2、Kiabana 2.3、Logstash 2.4、可以添加的其它组件:Filebeat 2.5、缓存/消息队列(redis、kafka、RabbitMQ等) 2.6、Fluentd 三、ELK工作原理 四、实例演示 1.ELK之 部署"E&q…

Leetcode 剑指Offer

求 12...n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。 示例 1: 输入: n 3 输出: 6示例 2: 输入: n 9 输出: 45 一、信息 1.求一个等差数列的求和 2.要求不能使…

lua环境搭建数据类型

lua作为一门计算机语言,从语法角度个人感觉还是挺简洁的接下来我们从0开始学习lua语言。 1.首先我们需要下载lua开发工具包 在这里我们使用的工具是luadist 下载链接为:https://luadist.org/repository/下载后的压缩包解压后就能用。 2.接下来就是老生…

ARM 相关概念2

一、汇编中三种符号(汇编指令、伪指令、伪操作) 二、汇编基本格式 三、数据操作指令 3.1 数据搬移指令mov/mvn ① 示例 ② 立即数 0xff000000 >判断的数 1111 1111 0000 0000 0000 0000 0000 0000 >判断的数 0000 0000 0000 0000 0000 0000 1111…

企业架构LNMP学习笔记51

企业案例使用: 主从模式: 缓存集群结构示意图: 去实现Redis的业务分离: 读的请求分配到从服务器上,写的请求分配到主服务器上。 Redis是没有中间件来进行分离的。 是通过业务代码直接来进行读写分离。 准备两台虚…

华为云云耀云服务器L实例评测|部署前后端分离项目

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 学习测评 ✨特色专栏: MyS…

2022年全国研究生数学建模竞赛华为杯D题PISA架构芯片资源排布问题求解全过程文档及程序

2022年全国研究生数学建模竞赛华为杯 D题 PISA架构芯片资源排布问题 原题再现: 一、背景介绍 芯片是电子行业的基础,在当前日益复杂的国际形势下,芯片成了各个大国必争的高科技技术。本课题关注网络通信领域的交换芯片,传统的交…

Python爬虫逆向猿人学刷题系列——第七题

题目:采集这5页中胜点列的数据,找出胜点最高的召唤师,将召唤师姓名填入答案中 地址:https://match.yuanrenxue.cn/match/7 本题主要是考察字体的动态变化,同样也是从字体文件下手构造出映射关系就好,但本题…

TuyaOS开发学习笔记(2)——NB-IoT开发SDK架构、运行流程

一、SDK架构 1.1 架构框图 基于 TuyaOS 系统,可以裁剪得到的适用于 NB-IoT 协议产品接入的 SDK。SDK 将设备配网、上下行数据通信、产测授权、固件 OTA 升级等接口进行封装,并提供相关函数。 1.2 目录结构 1.2.1 TuyaOS目录说明 adapter:T…

70、Spring Data JPA 的 自定义查询(全手动,自己写完整 SQL 语句)

1、方法名关键字查询(全自动,既不需要提供sql语句,也不需要提供方法体) 2、Query查询(半自动:提供 SQL 或 JPQL 查询) 3、自定义查询(全手动) ★ 自定义查询&#xff08…

范文展示,如何三步写出一篇满意的论文

第一步:输入文章关键信息 文章标题,写论文的话即为拟定的论文标题,例如这篇范文中的题目为“阳明心学研究” 关键词,可以写出多个论文主题相关的关键词,用逗号分开,例如这篇范文中只写了一个关键词“王阳…