Redis中的Lua脚本(二)

Lua脚本

创建排序辅助函数

为了防止带有副作用的函数令脚本产生不一致的数据,Redis对math库的math.random函数和math.randomseed函数进行了替换。对于Lua脚本来说,另一个可能产生不一致数据的地方是哪些带有不确定性质的命令,比如对于一个集合键来说,因为集合的排列是无序的,所以即使两个集合的元素完全相同,它们的输出结果也可能并不相同。

例子

  • 举个例子。
127.0.0.1:6379> SADD fruit apple banana cherry
(integer) 3
127.0.0.1:6379> SMEMBERS fruit
1) "banana"
2) "apple"
3) "cherry"
127.0.0.1:6379> SADD another-fruit cherry banana apple
(integer) 3
127.0.0.1:6379> SMEMBERS another-fruit
1) "apple"
2) "banana"
3) "cherry"

(高版本Redis可能已经内置了排序函数)
这个例子中的fruit集合和another-fruit集合包含的元素是完全相同的,只是因为集合添加的顺序不同,SMEMBERS命令的输出就产生了不同的结果。Redis将SMEMBERS这种在相同数据集上可能会产生不同输出的命令称为"带有不确定性的命令",这些命令包括:SINTER、SUNION、SDIFF、SMEMBERS、HKEYS、HVALS、KEYS为了消除这些命令带来的不确定性,服务器会为Lua环境创建一个排序辅助函数_redis_compare_helper,
当Lua脚本执行完一个带有不确定性的命令之后,程序会使用_redis_compare_helper作为对比函数,自动调用table.sort函数对命令的返回值做一次排序,以此来保证相同的数据集总是产生相同的输出

  • 举个例子。如果在Lua脚本中对fruit集合和another-fruit集合执行SMEMBERS命令,那么两个脚本将得出相同的结果,因为脚本已经对SMEMBERS命令的输出进行过排序了:
127.0.0.1:6379> EVAL "return redis.call('SMEMBERS', KEYS[1])" 1 fruit
1) "apple"
2) "banana"
3) "cherry"
127.0.0.1:6379> EVAL "return redis.call('SMEMBERS', KEYS[1])" 1 another-fruit
1) "apple"
2) "banana"
3) "cherry"

创建redis.pcall函数的错误报告辅助函数

服务器将为Lua环境创建一个名为_redis_err_handler的错误处理函数,当脚本调用redis.pcall函数执行Redis命令,并且被执行的命令出现错误时,_redis_err_handler_就会打印出错误代码的来源和发生错误的行数,为程序的调试提供方便。

例子

  • 举个例子。如果客户端要求服务器执行以下Lua脚本:
--1--2--3return redis.pcall('wrong command')

那么服务器将向客户端返回一个错误:

redis-cli --eval wrong-command.lua
(error) @user_script: 4: Unknown Redis command called from Lua script

其中@user_script说明这是一个用户定义的函数,而之后的4则说明出错的代码位于Lua脚本的第4行

保护Lua的全局环境

服务器将对Lua环境中的全局环境进行保护,确保传入服务器的脚本不会因为忘记使用local关键字而将额外的全局变量添加到Lua环境里面。
因为全局比那辆保护的原因,当一个脚本试图创建一个全局变量时,服务器将报告一个错误:

127.0.0.1:6379> EVAL "x = 10" 0
(error) ERR Error running script (call to f_df1ad3745c2d2f078f0f41377a92bb6f8ac79af0): @enable_strict_lua:8: user_script:1: Script attempted to create global variable 'x'

除此之外,试图获取一个不存在的全局变量也会引发一个错误:

127.0.0.1:6379> EVAL "return x" 0
(error) ERR Error running script (call to f_03c387736bb5cc009ff35151572cee04677aa374): @enable_strict_lua:15: user_script:1: Script attempted to access unexisting global variable 'x

不过Redis并未禁止用户修改已存在的全局变量,所以在执行Lua脚本的时候,必须非常消息,以免错误地修改了已存在地全局变量

127.0.0.1:6379> EVAL "redis = 10086; return redis" 0
(integer) 10086

将Lua环境保存到服务器状态的lua属性里面

在这里插入图片描述

服务器会将Lua环境和服务器状态的lua属性关联起来,如图所示.
因为Redis使用串行化的方式来执行Redis命令,所以在任何特定时间里,
最多都只会有一个脚本能够放进Lua环境里面运行,因此,整个Redis服务器只需要创建一个Lua环境即可

Lua环境协作组件

除了创建并修改Lua环境之外,Redis服务器还创建了两个用于与Lua环境进行协作的组件,它们分别是负责执行Lua脚本的Redis命令的伪客户端,以及用于保存Lua脚本的lua_scripts字典

伪客户端

因为执行Redis命令必须有相应的客户端状态,所以为了执行Lua脚本中包含的Redis命令,Redis服务器专门为Lua环境创建了一个伪客户端,并由这个伪客户端负责处理Lua脚本中包含的所有Redis命令。
Lua脚本使用redis.call函数或者redis.pcall函数执行一个Redis命令,需要完成以下步骤:

  • 1.Lua环境将redis.call函数或者redis.pcall函数想要执行的命令传给伪客户端
  • 2.伪客户端将脚本想要执行的命令传给命令执行器
  • 3.命令执行器执行伪客户端传给它的命令,并将命令的执行结果返回给伪客户端
  • 4.伪客户端接收命令执行器返回的命令结果,并将这个命令结果返回给Lua环境
  • 5.Lua环境在接收到命令结果之后,将该结果返回给redis.call函数或者redis.pcall函数
  • 6.接收到结果的redis.call函数或者redis.pcall函数会将命令结果作为函数返回值返回给脚本中的调用者

图中展示了Lua脚本在调用redis.call函数时,Lua环境、伪客户端、命令执行器三者之间的通信过程(调用redis.pcall函数时产生的通信过程也是一样的)在这里插入图片描述

例子
  • 举个例子。如图展示了Lua脚本在执行以下命令时:
127.0.0.1:6379> EVAL "return redis.call('dbsize')" 0
(integer) 3

Lua环境、伪客户端、命令执行器三者之间的通信过程如图所示
在这里插入图片描述

lua_script字典

Redis服务器为Lua环境创建的另一个协作组件是lua_scripts字典,这个字典的键为某个Lua脚本的SHA1校验和(checksum),而字典的值则是SHA1校验和对应的Lua脚本

struct redisServer {
// ...dict *lua_script;// ...
};

Redis服务器会将所有被EVAL命令执行过的Lua脚本,以及所有被SCRIPT LOAD命令载入过的Lua脚本都保存到lua_scripts字典里面。

例子
  • 举个例子,如果客户端向服务器发送以下命令:
127.0.0.1:6379> SCRIPT LOAD "return hi"
"728d9ecdcf934ba0f430b2b05f049e13041278ae"
127.0.0.1:6379> SCRIPT LOAD "return 1+1"
"a27e7e8a43702b7046d4f6a7ccf5b60cef6b9bd9"
127.0.0.1:6379> SCRIPT LOAD "return 2*2"
"4475bfb5919b5ad16424cb50f74d4724ae833e72"

那么服务器的lua_scripts字典将包含被SCRIPT LOAD命令载入的三个Lua脚本,如图所示。在这里插入图片描述

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

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

相关文章

面试八股——线程

进程与线程的对比 线程的创建方式⭐ 继承Thread类 实现Runnable接口 实现Callable类 适用于需要获取返回值的线程。 线程池创建线程 Callable与Runnable的区别 1. 首先Runnable的run方法是没有返回值的,Callable的call方法有返回值。 2. Runnable不能向外抛出异常…

混合云构建-如何创建一个高可用的Site to Site VPN 连接 Azure 和GCP云

在现代云计算环境中,企业通常会采用多云战略,将工作负载分布在不同的云服务提供商上。这种方式可以提高可用性、降低供应商锁定风险,并利用每个云提供商的独特优势。然而,在这种情况下,需要确保不同云环境之间的互联互通,以实现无缝的数据传输和应用程序集成。 本文将详细介绍…

Python根据公募基金在一定时期内持有的股票数据进行社会网络分析

【背景】根据提供的公募基金在一定时期内持有的股票数据,构建一个社会网络分析框架,度量每个基金在每年的度中心度、介数中心度和特征向量中心度,并对相关数据做出简要说明。 【代码】 import networkx as nx import pandas as pd import n…

SpringBoot整合Mybatis实现从数据库中读取blob属性的图片在html页面中无法显示并且出现乱码实体类,如何解决?

🏆本文收录于「Bug调优」专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&…

大屏数字字体+渐变色

vue数据大屏使用数字字体_vue数字字体-CSDN博客 用css实现文字字体颜色渐变的三种方法_css 字体颜色渐变-CSDN博客

华为OD技术面试-有序数组第K最小值

背景 2024-03-15华为od 二面,记录结题过程 有序矩阵中第 K 小的元素 - 力扣(LeetCode) https://leetcode.cn/problems/kth-smallest-element-in-a-sorted-matrix/submissions/512483717/ 题目 给你一个 n x n 矩阵 matrix ,其…

HackMyVM-Gift

目录 信息收集 arp nmap WEB dirsearch hydra ssh连接 get root 信息收集 arp ┌─[rootparrot]─[~] └──╼ #arp-scan -l Interface: enp0s3, type: EN10MB, MAC: 08:00:27:16:3d:f8, IPv4: 192.168.9.102 Starting arp-scan 1.10.0 with 256 hosts (https://git…

SpringBoot框架——7.整合MybatisPlus

这篇主要介绍Springboot整合MybatisPlus,另外介绍一个插件JBLSpringbootAppGen,以及一个经常用于测试的基于内存的h2数据库。 Mybatisplus是mybatis的增强工具,和tk-mybatis相似,但功能更强大,可避免重复CRUD语句,先来…

#无FIFO驱动OV7670基于cubemx(草稿)

1.前言 之前在淘宝买了一个不带FIFO的OV7670,由于比赛和其他事一直搁置,现在有时间于是想玩一玩。我发现网上这个的教程多为标准库,有些甚至利用了DCMI(数字摄像头接口,目前已知F4系列是有这个外设的)。标…

单链表的基本操作实现:初始化、尾插法、头插法、输出单链表、求表长、按序号查找、按值查找、插入结点、删除结点。

1.参考学习博文(写的相当好的文章): http://t.csdnimg.cn/AipNl 2.关于我的总结: 定义单链表: typedef struct LNode {Elemtype data;struct LNode* next; }LNode; data用来存放元素值,next用来指向后…

[Vision Board创客营]学习片上Flash移植FAL

文章目录 [Vision Board创客营]学习片上Flash移植FAL介绍环境搭建使用组件测试porbeerasewriteread 结语 [Vision Board创客营]学习片上Flash移植FAL 水平较菜,大佬轻喷。😰😰😰 介绍 🚀🚀Vision-Board 开…

高仿小米商城用户端

高仿小米商城用户端(分为商城前端(tongyimall-vue)和商城后端(tongyimall-api)两部分),是Vue SpringBoot的前后端分离项目,用户端包括首页门户、商品分类、首页轮播、商品展示、商品推荐、购物车、地址管理、下订单、扫码支付等功能模块。 …