Redis Lua脚本
文章目录
- 一.引言
- 二.eval简介
- 三.lua数据类型和redis数据类型之间转换
- 四.脚本的原子性
- 五.错误处理
- 六.纯函数脚本
- 七.选择内部脚本
一.引言
eval和evalsha命令使用内置的lua解释器,可以对lua脚本进行求值。
二.eval简介
- 第一个参数是一段脚本程序
- 第二个参数是参数的个数,后面的参数表示脚本中所用到的那些redis键,这些键名参数可以在lua中通过全局变量KEYS数据,以1为基址的形式访问(KEYS[1],KEYS[2]…)。
- 那些不是键名的附加参数,ARGV[],可以在lua中通过全局变量ARGV数组访问,访问的形式和KEYS变量类似(ARGV[1],ARGV[2]…)
注意:eval命令所有键都应该由KEYS数组来传递,因为不仅仅是eval命令,所有的redis命令在执行之前都会被分析,借此来确定命令会对哪些键进行操作。对于eval命令来说,必须使用正确的形式来传递键,才能确保分析工作正确地执行。而且可以确保redis集群可以将你的请求发送到正确的集群节点。
例如:
127.0.0.1:6379> eval "return {KEYS[1],ARGV[1]}" 1 foo bar
1) "foo"
2) "bar"
返回结果是redis multi bulk replies的lua数组,这是一个redis的返回类型,您的客户端可能会将他们转换成数组类型
lua脚本中使用lua函数调用redis命令的例子:
- redis.call()
- redis.pcall()
两种方式的区别:当reis命令执行结果返回错误是,redis.call()将返回给调用者一个错误,而redis.pcall()会将捕获的错误以lua表的形式返回。
三.lua数据类型和redis数据类型之间转换
当lua通过call()或pcall()函数执行redis命令的时候,命令的返回值会被转换成lua数据结构。同样地,当lua脚本在redis内置的解释器里运行时,lua脚本的返回值也会被转换成reids协议,然后由eval将值返回给客户端。
数据类型转换之间遵循这样一个设计原则:将一个redis值转换成lua值,之后再将转换所得的lua值转换回redis值,那么这个转换所得的redis值应该和最初的redis值一样。
redis->lua:
- redis integer reply -> lua number
- redis bulk reply -> lua string
- redis multi bulk reply -> lua table
- redis status reply -> lua table with a single err field containing the error
- redis error reply -> lua table with a single err field containing the error
- redis nil reply and nil multi bulk reply -> lua false boolean type
lua->redis:
- lua number -> redis integer reply
- lua string -> redis bulk reply
- lua table -> redis multi bulk reply
- lua table with a single ok field -> redis status reply
- lua table with a single err field -> redis error reply
- lua boolean false -> redis nil bulk reply
特殊lua->redis转换:
- redis boolean true -> redis integer reply with value of 1
注意:
- lua中整数和浮点数之间没有任何区别。因此,我们如果将lua的数字转换成整数的回复,这样将舍去小数部分。如果想从lua返回一个浮点数,你应该将它视作一个字符串。
- nil 作为一个回复的结束,后面的值将不会被返回
例子:
> eval "return {1,2,{3,'Hello World!'}}" 0
1) (integer) 1
2) (integer) 2
3) 1) (integer) 32) "Hello World!"> eval "return redis.call('get','foo')" 0
"bar"> eval "return {1,2,3.3333,'foo',nil,'bar'}" 0
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) "foo"
四.脚本的原子性
redis使用单个lua脚本解释器去运行所有脚本,并且redis也播啊这呢个脚本会以原子性的方式执行:当某个脚本正在运行的时候,不会有其他脚本或者redis命令被执行。这和使用MULTI/EXEC包围的事物很类似。在其他客户端看来,脚本的效果只能是不可见或是已完成。要注意慢脚本的问题,一个蜗牛脚本执行是,其他客户端会因为服务器正忙而无法执行命令。
五.错误处理
-
当redis.call()在执行命令的过程中发生错误时,脚本会停止执行,并返回一个脚本错误
> del foo (integer) 1 > lpush foo a (integer) 1 > eval "return redis.call('get','foo')" 0 (error) ERR Error running script (call to f_6b1bf486c81ceb7edf3c093f4c48582e38c0e791): ERR Operation against a key holding the wrong kind of value -
和redis.call()不同,redis.pcall()出错时并不引发错误,而是返回一个带err域的lua表,用于表示错误
redis 127.0.0.1:6379> EVAL "return redis.pcall('get', 'foo')" 0 (error) ERR Operation against a key holding the wrong kind of value
六.纯函数脚本
脚本应该被写成纯函数脚本。脚本应该具有以下属性:
- 对于同样的数据集输入,给定相同的参数,脚本的redis写命令总是相同的。脚本执行的操作不能依赖于任何吟唱(非显式)数据,不能依赖于脚本在执行过程中、或在不同执行时间之间可能变更的状态,并且也不能依赖于任何来自IO设备的外部输入。
使用系统时间,调用randomkey那样的随机命令,或者使用lua的随机数生成器,渴死以上的这些操作,都会遭整脚本的求值无法每次都得出同样的结果。为了保证上述脚本的属性,redis做了以下工作:
- lua没有方位系统时间或者其他内部状态的命令
- 执行随机命令(randomkey,time)时,redis会返回一个错误,阻止这样的脚本运行
- lua脚本中调用哪些返回无序元素的命令时,执行命令所得的数据在返回给lua之前会先执行一个静默的字典排序。如调用:redis.call(“smembers”, KEYS[1]) ,返回的总是排过序的元素
- 对lua的伪随机数生成函数math.random,math.randomseed进行修改,使得每次在运行新脚本的时候总是拥有同样的seed值。者意味着,每次运行脚本是,只要不使用math。randomseed,那么math.random产生的随机数序列总是相同的
为了防止不必要的数据泄漏进lua环境,redis脚本不允许创建全局变量。如果一个脚本需要在多次执行之间维持某种状态,它应该使用reids key来进行状态保存,企图在脚本中访问一个全局变量将引起脚本停止,eval命令会返回一个错误。
七.选择内部脚本
lua脚本之影响脚本本身的执行,但不修改当前客户端调用脚本时选定的数据库。
可用库:
-
base lib
-
table lib
-
string lib
-
math lib
-
debug lib
-
struct lib
拆装箱处理
127.0.0.1:6379> eval 'return struct.pack("HH", 1, 2)' 0 "\x01\x00\x02\x00" 127.0.0.1:6379> eval 'return {struct.unpack("HH", ARGV[1])}' 0 "\x01\x00\x02\x00" 1) (integer) 1 2) (integer) 2 3) (integer) 5 127.0.0.1:6379> eval 'return struct.size("HH")' 0 (integer) 4 -
cjson lib
json处理
redis 127.0.0.1:6379> eval 'return cjson.encode({["foo"]= "bar"})' 0 "{\"foo\":\"bar\"}" redis 127.0.0.1:6379> eval 'return cjson.decode(ARGV[1])["foo"]' 0 "{\"foo\":\"bar\"}" "bar" -
cmsgpack lib
简单快速的message pack操纵
127.0.0.1:6379> eval 'return cmsgpack.pack({"foo", "bar", "baz"})' 0 "\x93\xa3foo\xa3bar\xa3baz" 127.0.0.1:6379> eval 'return cmsgpack.unpack(ARGV[1])' 0 "\x93\xa3foo\xa3bar\xa3baz" 1) "foo" 2) "bar" 3) "baz" -
bittop lib
为lua位运算模块增加了按位操作数
127.0.0.1:6379> eval 'return cmsgpack.pack({"foo", "bar", "baz"})' 0 "\x93\xa3foo\xa3bar\xa3baz" 127.0.0.1:6379> eval 'return cmsgpack.unpack(ARGV[1])' 0 "\x93\xa3foo\xa3bar\xa3baz" 1) "foo" 2) "bar" 3) "baz" -
redis.sha1hex function
相关文章:
Redis Lua脚本
文章目录一.引言二.eval简介三.lua数据类型和redis数据类型之间转换四.脚本的原子性五.错误处理六.纯函数脚本七.选择内部脚本一.引言 eval和evalsha命令使用内置的lua解释器,可以对lua脚本进行求值。 二.eval简介 第一个参数是一段脚本程序第二个参数是参数的个…...
web自动化测试-执行 JavaScript 脚本
JavaScript 是一种脚本语言,有的场景需要使用 js 脚本注入辅助我们完成 Selenium 无法做到的事情。 当 webdriver 遇到无法完成的操作时,可以使用 JavaScript 来完成,webdriver 提供了 execute_script() 方法来调用 js 代码。 执行 js 有两种…...
libevent笔记——简单介绍
背景 libevent libevent – an event notification library 官方定义:libevent是一个事件通知的库。更详细的介绍参考官方的就够了,这里我摘抄一下,并做一些注释 The libevent API provides a mechanism to execute a callback function whe…...
C++学习笔记-多态
多态的概念 多态的概念:通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会 产生出不同的状态 。 举个例子:比如 买票这个行为 ,当 普通人 买票时,是全价买票;…...
5632: 三角形
描述平面坐标系下,给定不共线的三个点组成一个三角形,问三角形最短的边长和最长的边长各为多少?输入输入包含3行,每行两个整数,表示一个点的坐标x和y。输出输出包括2个小数,分别为最短的边长和最长的边长。…...
Java基础--IO操作
一、IO原理及分类 一、IO原理 1、I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理设备之间的数据传输,如读写文件,网络通信等。 2、java程序中对于数据的输入/输出操作一般都是以流的方式进行 3、java.io包下提供各…...
C++多线程
目录一、C线程库1. 认识thread类2. 线程函数的参数3. this_thread二、原子操作三、C互斥锁1. mutex2. lock_guard3. unique_lock四、C条件变量1. condition_variable2. 实现两个线程交替打印奇偶数一、C线程库 1. 认识thread类 在C11之前没有多线程的概念,涉及到的…...
【Arduino使用nRF24L01 】
【Arduino使用nRF24L01 】 1. 概述2. nRF24L01 收发器模块2.1工作原理2.2 NRF24L01模块变体2.3 nRF24L01 模块引脚排列3. 如何将 nRF24L01 连接到 Arduino3.1 原理接线图3.2 Arduino 和 nRF24L01 代码3.3 代码说明4. 故障排除5. 两个NRF24L01和Arduino进行双向无线通信5.1 nRF2…...
Appium自动化测试框架是一种较为优雅的使用方式
以操作小米商城下单为例流程是 启动小米商城app, 点击分类,点击小米手机, 点击小米10 至尊版,点击加入购物车,点击确定....原脚本Copyfrom time import sleep from appium import webdriver from selenium.common.exceptions impo…...
Linux c编程之应用交互协议分析与设计
在实际编程应用中,两个或多个功能服务(模块)之间 需要通过消息交互进行协作完成用户想要的逻辑功能,这里的消息交互指的是应用层的交互。最终数据传输(无论是TCP/IP还是其它)都是以二进制形式完成,但对于应用层协议来说有两种,一种是二进制协议,一种是文本协议。不管是…...
基于YOLOv5的细胞检测实战
数据及代码链接见文末 1.任务与数据集介绍 如下图所示,我们有一个医学细胞数据集,需要从数据集中检测出三种不同的细胞。标签中已经标注了细胞的类别和位置。 我们也可以看到,三种细胞有着不同的形态和颜色,同时数据集的标签也存在没有标注到的细胞 2.数据与标签配置方…...
【经典蓝牙】蓝牙AVRCP协议分析
协议简介 蓝牙AVRCP协议是蓝牙设备之间音视频的控制协议。定义了音频/视频的控制、浏览、查询、通知等一系列的命令集。常用来蓝牙耳机对手机的音乐进行控制,以及获取手机的音乐信息等场景。AVRCP协议有两个角色,分别是controller(CT&#x…...
gin 框架初始教程
一 、gin 入门1. 安装gin :下载并安装 gin包:$ go get -u github.com/gin-gonic/gin2. 将 gin 引入到代码中:import "github.com/gin-gonic/gin"3.初始化项目go mod init gin4.完整代码package mainimport "github.com/gin-go…...
对象分配策略
对象创建后,究竟何去何从,对象在堆中又会经历哪些过程,本篇就会详细解释对象创建后直到对象被回收的整个过程。之前博主已经写过Minor GC、Major GC、Full GC的区别,而本篇也主要根据这几个GC开展。 对象回收过程流程如下图所示: 正常的对象生存过程&a…...
你可能不知道的前端监控方案
前言 现有的大部分监控方案都是针对服务端的,而针对前端的监控很少,诸如线上页面的白屏时间是多少、静态资源的加载情况如何、接口请求耗时好久、什么时候挂掉了、为什么挂掉,这些都不清楚。 因而,我们需要一个前端的页面监控系…...
java spring AOP 完全注解开发
我们先创建一个项目 然后引入java spring aop的依赖 然后 在src下创建目录 我这里 直接就叫 Aop了 下面创建一个User类 参考代码如下 package Aop;import org.springframework.stereotype.Component;Component public class User {public void add(){System.out.println(&qu…...
ctf pwn基础-4
今天是学pwn的第四天,去接触了pwn的整数溢出。 目录 基础 实例讲解 实例讲解2 基础 关于整数溢出,这里以int为例,因为我php之前搞的比较多,以为这个int也是想php一样是64,最大值是9开头的那个,闹了不少笑…...
bool与引用类型
bool与引用类型bool类型介绍与使用bool(布尔类型)大小:1个字节返回值有两个:1(true),0(false)#include<iostream>using namespace std;int main() {bool a false;bool b true;cout << "a " << a << end…...
tkinter界面的TCP通信/tkinter开启线程接收TCP
前言 用简洁的语言写一个可以与TCP客户端实时通信的界面。之前做了一个项目是要与PLC进行信息交互的界面,在测试的时候就利用TCP客户端来实验,文末会附上TCP客户端。本文分为三部分,第一部分是在界面向TCP发送数据,第二部分是接收…...
[SQL Statements] 基本的SQL知识 之DDL针对数据库的基本操作
SQL Statements SQL语句的学习 之 DDL针对数据库的基本操作 什么是database 在 MySQL 中,Database(数据库)是一组有组织的数据集合,可以存储和管理相关数据的容器。一个数据库可以包含多个表(Table)&…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
