Redis中的Lua脚本(一)

Lua脚本

概述

Redis从2.6版本开始引入对Lua脚本的支持,通过在服务器中嵌入Lua环境,Redis客户端可以使用Lua脚本,直接在服务器端原子地执行多个Redis命令。其中使用EVAL命令可以直接对输入的脚本进行求值:

127.0.0.1:6379> EVAL "return 'hello world'" 0
"hello world"

而使用EVALSHA命令则可以根据脚本的SHA1校验和来对脚本进行求值,但这个命令要求校验和对应的脚本必须至少被EVAL命令执行过
一次:

127.0.0.1:6379> EVAL "return 1+1" 0
(integer) 2
127.0.0.1:6379> EVALSHA "a27e7e8a43702b7046d4f6a7ccf5b60cef6b9bd9" 0 // 上一个脚本的校验和
(integer) 2

或者这个校验和对应的脚本曾经被SCRIPT LOAD命令载入过:

127.0.0.1:6379> SCRIPT Load "return 2*2"
"4475bfb5919b5ad16424cb50f74d4724ae833e72"
127.0.0.1:6379> EVALSHA "4475bfb5919b5ad16424cb50f74d4724ae833e72" 0
(integer) 4

创建并修改Lua环境

为了在Redis服务器中执行Lua脚本,Redis在服务器内嵌了一个Lua环境(evnironment),并对这个Lua环境进行了一系列修改,从而确保这个Lua环境可以满足Redis服务器的需要。Redis服务器创建并修改Lua环境的整个过程由以下步骤组成:

  • 1.创建一个基础的Lua环境,之后的所有修改都是针对这个环境进行的。
  • 2.载入多个函数库到Lua环境里面,让Lua脚本可以使用这些函数库来进行数操作
  • 3.创建全局表格redis,这个表格包含了对Redis进行操作的函数,比如用于在Lua脚本中执行Redis命令的redis.call函数
  • 4.使用Redis自制的随机函数来替换Lua原有的带有副作用的随机函数,从而避免在脚本中引入副作用
  • 5.创建排序辅助函数,Lua环境使用这个辅佐函数来对一部分Redis命令的结果进行排序,从而消除这些命令的不确定性
  • 6.创建redis.pcall函数的错误报告辅助函数,这个函数可以提供更详细的出错信息
  • 7.对Lua环境中的全局环境进行保护,防止用户在执行Lua脚本的过程中,将额外的全局变量添加到Lua环境中
  • 8.将完成修改的Lua环境保存到服务器状态的Lua环境中,等待执行服务器传来的Lua脚本

创建Lua环境

服务器首先调用Lua的C API函数lua_open,创建一个新的Lua环境。因为lua_open函数创建的只是一个基本的Lua环境,为了让这个Lua环境可以满足Redis的操作要求,接下来服务器将对这个Lua环境进行一系列修改。

载入函数库

Redis修改Lua环境的第一步,就是将以下函数库载入到Lua环境里面:

  • 1.基础库(base library):这个库包含Lua的核心(core)函数,比如assert、error、pairs、tostring、pcall等。另外,为了防止用户从外部文件中引入不安全的代码,库中的loadfile函数会被删除
  • 2.表格库(table library):这个库包含用于处理表格的通用函数。比如table.concat、table.insert、table.remove、table.sort等
  • 3.字符串库(string library):这个库包含用于处理字符串的通用函数,比如用于对字符串进行查找的string.find函数,对字符串进行格式化的string.format函数,查看字符串长度的string.len函数,对字符串进行反转的string.reverse函数等
  • 4.数据库(math libraray):这个库是标准C语言数据库的结构,它包括计算绝对值的math.abs函数,返回多个数中的最大值和最小值的math.max函数和math.min函数,计算二次方根的math.sqrt函数,计算对数的math.log函数等
  • 5.调试库(debug libraray):这个库提供了对程序进行调试所需的函数,比如对程序设置钩子和取得钩子的debug.sethook函数和debug.gethook函数,返回给定函数相关信息的debug.getinfo函数,为对象设置元数据的debug.setmetatable函数,获取对象元数据的debug.getmetatable函数等
  • 6.Lua CJSON库:这个库用于处理UTF-8编码的JSON格式,其中cjson.decode函数将一个JSON格式的字符串转换为一个Lua值,
    而cjson.encode函数将一个Lua值序列化为JSON格式的字符串
  • 7.Struct库:这个库用于在Lua值和C结构(struct)之间进行转换,函数struct.pack将多个Lua值打包成一个类结构(struct-like)字符串,而函数struct.unpack则从一个类结构字符串中解包出多个Lua值
  • 8.Lua cmsgpack库:这个库用于处理MessagePack格式的数据,其中cmsgpack.pack函数将Lua值转换为MessagePack数据,而cmsgpack.unpack函数则将messagepack数据转换为Lua值通过使用这些功能强大的函数库,Lua脚本可以直接对执行Redis命令获得的数据进行复杂的操作

创建Redis全局

服务器将在Lua环境中创建一个redis表格(table),并将它设置为全局变量,这个redis表格包含以下函数:

  • 1.用于执行Redis命令的redis.call和redis.pcall函数
  • 2.用于记录Redis日志(log)的redis.log函数,以及相应的日志级别(level)常量:redis.LOG_DEBUG,redis.LOG_VERBOSE,redis.LOG_NOTICE以及redis.LOG_WARNING
  • 3.用于计算SHA1校验和的redis.sha1hex函数
  • 4.用于返回错误信息的redis.error_reply函数和redis.status_reply函数。
    在这些函数里面,最常用也最重要的要数redis.call函数和redis.pcall函数,通过这两个函数,用户可以直接在Lua脚本中执行Redis命令:
127.0.0.1:6379> EVAL "return redis.call('ping')" 0
PONG

使用Redis自制的随机函数来替换Lua原有的随机函数

为了保证相同的脚本可以在不同的机器上产生相同的结果,Redis要求所有传入服务器的Lua脚本,以及Lua环境中的所有函数,都必须是无副作用(side effect)的纯函数(pure function).但是,在之前载入Lua环境的match函数库中,用于生成随机数的math.random函数和math.randomseed函数都是带有副作用的,它们不符合Redis对Lua环境的无副作用要求。因为这个原因,Redis使用自制的函数替换了math库中原有的math.random函数和math.randomseed函数,替换之后的两个函数有以下特征:

  • 1.对于相同的seed来说,math.random总产生相同的随机数绪列,这个函数是一个纯函数
  • 2.除非在脚本中使用math.randomseed显示地修改seed,否则每次运行脚本时,Lua环境都使用固定地math.randomseed(0)语句来初始化seed
例子
  • 举个例子。使用以下脚本,我们可以打印seed值为0时,math.random对于输入10至1所产生地随机绪列
--random-with-default-seed.lua

local i = 10
local seq ={}
while (i > 0) do
seq[i] = math.random(i)
i = i+1
end

return seq

无论执行这个脚本多少次产生的值都是相同的

E:\redis>redis-cli --eval test.lua
1) (integer) 1
2) (integer) 2
3) (integer) 2
4) (integer) 3
5) (integer) 4
6) (integer) 4
7) (integer) 7
8) (integer) 1
9) (integer) 7
10) (integer) 2

但是如果我们在另一个脚本里面调用math.randomseed将seed修改为10086

--random-with-default-seed.lua

math.randomseed(10086) -- change seed
local i = 10
local seq ={}
while (i > 0) do
seq[i] = math.random(i)
i = i-1
end

return seq

那么这个脚本生成的随机数绪列将和使用默认seed值0时生成的随机绪列不同:

E:\redis>redis-cli --eval test1.lua
1) (integer) 1
2) (integer) 1
3) (integer) 2
4) (integer) 1
5) (integer) 1
6) (integer) 3
7) (integer) 1
8) (integer) 1
9) (integer) 3
10) (integer) 1

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

51-41 Stable Video Diffusion,高质量视频生成新时代

23年11月,Stability AI公司公开了稳定视频扩散模型Stable Video Diffusion(SVD)的代码和权重,视频生成迎来了新时代。SVD是一种潜在扩散模型,支持文本生成视频、图像生成视频以及物体多视角3D合成。从工程角度来看,本文主要提出了…

C++如何使用string类

文章目录 为什么要学习string?库中的string关于编码ASCII编码Unicode编码 迭代器Iteratorsstring常用构造接口接口声明与功能说明接口演示 string类对象的容量操作接口声明与功能说明接口演示reverse与resize在不同平台下的扩容与缩容机制 string类对象的访问及遍历操作接口声…

Java项目实现图形验证码(Hutool)

项目架构: 使用SpringCloudmysqlmybatis-plus需要将数据库中的数据导出到Excel文件中 前端为Vue2 业务场景: 登录时使用验证码登录 1.1 打开hutool, 搜索 图片验证码 1.2后端编写生产验证码方法 1.3前端 1.3.1展示验证码 1.3.2 前端方法 1.3.2.1UU…

Django中的数据库优化与ORM性能调优【第169篇—ORM性能调优】

👽发现宝藏 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Django中的数据库优化与ORM性能调优 在开发基于Django的Web应用程序时,数据库是…

ubuntu 查询mysql的用户名和密码 ubuntu查看username

ubuntu 查询mysql的用户名和密码 ubuntu查看username 文章标签mysqlUbuntu用户名文章分类MySQL数据库 一.基本命令 1.查看Ubuntu版本 $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 16.04.5 LTS Release: 16.04 Coden…

leetcode-分割链表

题目 面试题 02.04. 分割链表 提示 给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你不需要 保留 每个分区中各节点的初始相对位置。 示例 1: 输入&#xff…

linux-centos虚拟机设置固定ip

环境准备 虚拟机版本:centos7 安装环境:vmware17 1、设置网络连接 虚拟机-设置-网络适配器-NAT模式 2、查看子网信息 编辑-虚拟网络编辑器-NAT模式-NAT设置 查看子网ip和网关ip 下一步要用 3、修改配置文件 vim /etc/sysconfig/network-scripts…

BGP边界网关路由实验(华为)

一,技术简介 BGP(边界网关路由协议)是一种自治系统(AS)间的协议,主要用于在不同的AS之间交换路由信息。AS是一个由一组网络设备和路由器组成的网络集合,这些设备可以在一个共同的管理域中协同工…

Netty-NioServerSocketChannel与NioSocketChannel

NioServerSocketChannel NioServerSocketChannel是netty服务端的channel。在ServerbootStrap的bind方法中,通过反射,实例化对象NioServerSocketChannel。   NioServerSocketChannel对象实例化的过程中。 AbstractChannel中实例化channel的id&#xff…

【QT进阶】Qt Web混合编程之QWebEngineView基本用法

往期回顾 【QT入门】Qt自定义控件与样式设计之自定义QTabWidget实现tab在左,文本水平的效果-CSDN博客【QT进阶】Qt Web混合编程之CEF、QCefView简单介绍-CSDN博客 【QT进阶】Qt Web混合编程之VS2019 CEF的编译与使用-CSDN博客 【QT进阶】Qt Web混合编程之QWebEngi…

通过Idea部署Tomcat服务器

1.在idea中创建项目 有maven构建工具就创建maven,没有就正常创建一个普通的java程序 创建普通java项目 2.添加框架 3.配置 Tomcat 注意:创建web项目后我们需要配置tomcat才能运行,下面我们来进行配置。 4.添加部署 回到服务器 5.完善配置 6…

EFK环境搭建(基于K8S环境部署)

目录 一.环境信息二.安装nfs供应商三.安装elasticsearch四.安装kibana组件五.安装fluentd 一.环境信息 1.服务器及k8s版本 IP地址主机名称角色版本192.168.40.180master1master节点1.27192.168.40.181node1node1节点1.27192.168.40.182node2node2节点1.27 2.部署组件版本 序…

Python 数据结构和算法实用指南(二)

原文:zh.annas-archive.org/md5/66ae3d5970b9b38c5ad770b42fec806d 译者:飞龙 协议:CC BY-NC-SA 4.0 第四章:列表和指针结构 我们已经在 Python 中讨论了列表,它们方便而强大。通常情况下,我们使用 Python…

近端安全互联样例使用指导

样例介绍 本样例基于rk3568开发板,通过封装openharmony安全子系统deviceauth组件提供的能力,实现了一组可用于设备间快速建立可信认证和连接的接口,通过预先定义关系网,在设备初始化阶段完成端端设备间的认证,构建安全…

ES源码四:网络通信层流程

听说ES网络层很难?今天来卷它😄 前言 ES网络层比较复杂,分为两个部分: 基于HTTP协议的REST服务端基于TCP实现的PRC框架 插件化设计的网络层模块(NetworkModule) 入口还是上一章的创建Node构造方法的地方…

目标检测应用场景—数据集【NO.31】布匹数据集目标检测数据集

写在前面:数据集对应应用场景,不同的应用场景有不同的检测难点以及对应改进方法,本系列整理汇总领域内的数据集,方便大家下载数据集,若无法下载可关注后私信领取。关注免费领取整理好的数据集资料!今天分享…

uniapp picker 多列选择器用法

uniapp picker 多列选择器联动筛选器交互处理方法, uniapp 多列选择器 mode"multiSelector" 数据及筛选联动交互处理, 通过接口获取数据,根据用户选择当前列选项设置子列数据,实现三级联动效果, 本示例中处…

【honggfuzz学习笔记】honggfuzz的基本特性

本文架构 1.动机2.honggfuzz的基本概念官网描述解读 3. honggfuzz的反馈驱动(Feedback-Driven)软件驱动反馈(software-based coverage-guided fuzzing)代码覆盖率代码覆盖率的计量单位 代码覆盖率的统计方式 硬件驱动反馈( hardware-based co…

IDEA 安装、基本使用、创建项目

文章目录 下载基本使用修改颜色主题Keymap插件 创建项目创建模块新建 Java 类运行新建 Package打包 Jar运行 jar 包 查看文档 下载 官方下载地址:https://www.jetbrains.com/zh-cn/idea/download/?sectionmac 这里我下载 macOS 社区版,IDEA 2024.1 (C…

60道计算机二级模拟试题选择题(含答案和解析)

点击下载《60道计算机二级模拟试题选择题(含答案和解析)》 1. 前言 本文设计了一份针对计算机二级考试的选择题,旨在考察考生对计算机基础知识和应用技能的掌握情况。试题涵盖了计算机基础知识、操作系统、办公软件、计算机网络等多个方面&…
最新文章