百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

Redis中的Lua脚本怎么玩

wptr33 2025-01-17 13:13 19 浏览

Redis中的Lua脚本怎么玩

Lua是一门强大、快速、轻量的嵌入式脚本语言,我们日常开发中接触的最多的还是Redis为保证原子性使用Lua执行多命令的一种方法,那么现在先来熟悉Lua基本用法。

Lua安装

Lua现在最新版本5.4.4,官网地址https://www.lua.org/

-- 解压
tar zxf lua-5.4.4.tar.gz
-- 进入lua解压文件主目录
cd lua-5.4.4
-- 编译
make all test

Lua官网最新是5.4的,如果想参考中文文档可以按照5.3的版本来,中文翻译地址https://www.runoob.com/manual/lua53doc/

Lua的基本用法

Lua的基本命令都可以在Lua官方提供的测试地址进行命令测试https://www.lua.org/cgi-bin/demo

Lua申明类型

Lua中分为全局变量和局部变量,一般建议将变量定义为布局变量,效率更高

--- 全局变量 
name = 'felord.cn'
--- 局部变量
local age = 18

Lua数据类型

Lua中包含的数据类型有8种,string、boolean、nil、table、number、userdata、function、thread,如果仅在Redis中使用那么function、thread、userdata不建议使用,只使用其余五种就行。

这五种中,最难理解的应该是table,table和Java中提到的Hash类型有点类似,但并不是完全相同。

arr = {'zhangsan','lisi',1,true,2.1}
print(arr[1]) -- table下标从1开始
print(arr[3])
print(arr[6])
print(#arr) -- 获取table的长度
---------- 结果
zhangsan
1
nil
5

采用字典模式时,用法不一样

--- 定义字典类型
arr = {name='zhangsan',age=12,sex='男'}
print(arr[1]) -- 普通取值无效
print(arr['name'])
print(#arr) -- 常规获取table长度失效
----------- 结果
nil
zhangsan
0

采用混合模式时

-- 采用混合模式定义table
arr = {name='zhangsan',age=12,1,sex='男',2.2}
print(arr[3])-- 字典类型不会统计所以arr[**]这种格式只能针对普通值
print(arr[1])--- 能够查询
print(arr[2])--- 能够查询
print(#arr)--- 只能查询普通值
print(arr['age'])
print(arr['sex'])
-----------结果
nil
1
2.2
2
12
男

在计算table的长度时,不能仅仅通过#arr获取,因为可能存在混合模式的情况,在不清楚元素类型的情况下建议采用循环获取table长度

Lua判断语句

-- 定义局部变量
local a = 21
if a < 10 then
   print('a<10')
elseif a < 20 then
   print('20<a<=10')
else 
   print('a>=20')
end

Lua循环判断

local arr = {22,23,44,name='zhangsan'}
for i,v in ipairs(arr) do
  print('i = '..i)-- 下标
  print('v = '..v)-- 值
end
-- 结果 只能循环普通值,不能循环字典属性
i = 1
v = 22
i = 2
v = 23
i = 3
v = 44

通用循环可以循环字典属性

local arr = {22,23,44,name='zhangsan'}
-- pairs通用循环可以循环字典属性,ipairs只能循环普通值
for i,v in pairs(arr) do
  print('i = '..i)
  print('v = '..v)
end
--- 结果
i = 1
v = 22
i = 2
v = 23
i = 3
v = 44
i = name
v = zhangsan

Redis中Lua使用

Redis从2.6.0版本开始支持Lua脚本,在Redis中使用不需要另外安装Lua程序,Redis内嵌了Lua。

EVAL

eval定义Redis执行的命令,格式为EVAL script numkeys key [key ...] arg [arg ...]

-- 在EVAL的script中添加Lua脚本时,keys代表键值,argv代表value值是全局运行变量不能写错
-- numkeys代表键的个数,是必须的参数,不能写错
127.0.0.1:6379> EVAL "return redis.call('set',KEYS[1],ARGV[1])" 1 name zhagnsan
OK
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> get name
"zhagnsan"
127.0.0.1:6379> EVAL "return redis.call('get',KEYS[1])" 1 name
"zhagnsan"

让redis执行lua脚本有两种形式,call和pcall

-- call正常返回错误信息,不做任何处理
127.0.0.1:6379> EVAL "return redis.call('no_command')" 0
(error) ERR Error running script (call to f_1e6efd00ab50dd564a9f13e5775e27b966c2141e):
@user_script:1: @user_script: 1: Unknown Redis command called from Lua script
-- pcall返回处理好的错误信息
127.0.0.1:6379> EVAL "return redis.pcall('no_command')" 0
(error) @user_script: 1: Unknown Redis command called from Lua script

注意点

在Redis中使用Lua时需要注意两点,Redis官方也提到这两点

精度丢失问题

在Redis中执行Lua脚本,因为是两个不同的编程环境,Lua脚本并不是区分整数和浮点数,所以Lua脚本将Lua数值转换为Redis中的值时会丢失精度,其它类型相互转换可以参考官网

-- 在将Lua中的值返回Redis
127.0.0.1:6379> EVAL "return {1,4,3.33,'zhangsan'}" 0
1) (integer) 1
2) (integer) 4
3) (integer) 3 -- 精度丢失
4) "zhangsan"
-- 可以之间返回字符串,这样精度不会丢失
127.0.0.1:6379> EVAL "return {1,4,'3.33','zhangsan'}" 0
1) (integer) 1
2) (integer) 4
3) "3.33" 
4) "zhangsan"
-- 或者使用字符串转换函数
127.0.0.1:6379> EVAL "return {1,4,tostring(3.33),'zhangsan'}" 0
1) (integer) 1
2) (integer) 4
3) "3.33"
4) "zhangsan"

nil值处理

在前面提到Lua的8大基本数据类型就包含了nil这个类型,这个类型是Lua所特有的,无法转换到Redis中,如果在转换时遇到了nil这个值,这样将导致转换停止

127.0.0.1:6379> EVAL "return {1,4,'3.33',nil,'zhangsan'}" 0
1) (integer) 1
2) (integer) 4
3) "3.33" -- 本来后面应该还有zhangsan这个数据,但是遇到了nil转换直接停止了

Lua脚本

Lua脚本的执行都是原子性的,所以Lua脚本执行势必阻塞其它线程,那么Lua脚本不宜过大,过大会带来其它资源的消耗,也不宜将一些复杂逻辑放入Lua脚本中。

SCRIPT LOAD

Lua脚本的预加载,避免多次传输,可以重复使用

127.0.0.1:6379> SCRIPT LOAD "return 'hello lua'"
"aeebf56de5e46b1d6f9e154df45402368df5a1d8" -- 得到唯一字符串表示
-- 配合EVALSHA使用,和EVAL类似可以传递传参
127.0.0.1:6379> EVALSHA aeebf56de5e46b1d6f9e154df45402368df5a1d8 0
"hello lua"

SCRIPT EXISTS

检查脚本是否存在

-- 存在返回1,不存在返回0
127.0.0.1:6379> SCRIPT EXISTS aeebf56de5e46b1d6f9e154df45402368df5a1d8
1) (integer) 1

SCRIPT KILL

终止正在执行的脚本,如果当终止脚本执行写逻辑还未结束,这时SCRIPT KILL命令是无效的,因为这违反了Lua的原子性规则,这种情况可以使用SHUTDOWN NOSAVE命令强制结束

SCRIPT FLUSH

清空脚本缓存

SCRIPT DEBUG

如果测试bug有误还可以开启debug模式

127.0.0.1:6379> SCRIPT DEBUG yes
OK
127.0.0.1:6379> SCRIPT help
 2) DEBUG (YES|SYNC|NO)
 3)     Set the debug mode for subsequent scripts executed.

相关推荐

威信Chronosonic XVX全新旗舰全球首发 设计特点彻底公开

第一眼看到WilsonAudio新推出的ChronosonicXVX音箱,相信大家都会直觉认为它是两年前超级旗舰WAMMMasterChronosonic的缩小版,不过这个推测并不完全正确。C...

C#高精度Timer和Delay以及时间测量

在PCHMI7.0后在工具箱里会多一个MsTimer,以及Delay和Microsecond两个类。...

python教程从基础到精通,第9课—日期与时间

Hello,小伙伴们,祝大家五.一玩得快乐!刚学习完了七大数据类型,今天咱们来学习日期与时间的表示方法。Python标准库中提供了时间和日期的支持:calendar:日历相关;time、datetim...

软件测试|教你轻松玩转Python日期时间

Python基础之日期时间处理...

Go语言中互斥锁与读写锁,你知多少?

简述Golang中的锁机制主要包含互斥锁和读写锁互斥锁互斥锁是传统并发程序对共享资源进行控制访问的主要手段。在Go中主要使用sync.Mutex的结构体表示。一个简单的示例:funcmutex()...

变形金刚动画大电影——经典台词赏析

YOURDAYSARENUMBEREDNOW,DECEPTI-CREEPS你们活不了多久了,霸天虎小子。-{铁皮说的话,体现了铁皮的嫉恶如仇,可是后来铁皮在飞船上遇袭身亡,可谓是出师未捷身先...

Python时间日期模块使用教程(python3日期)

1.时间日期处理概述在日常编程中,时间日期处理是非常常见的需求,比如:记录日志时间...

亚马逊介绍AWS“无服务器”云服务改进:数据库可线上扩充容量等

IT之家11月29日消息,在今天于美国拉斯维加斯展开的亚马逊“AWSre:Invent2023”活动中,亚马逊计算部门资深副总裁PeterDeSantis,介绍了旗下三款云端服务,IT...

2.日期格式 datetime(日期时间显示格式)

fromdatetimeimportdatetime1.获取当前日期和时间now=datetime.now()#2025-05-3110:56:01.4687822.格式化日期...

【科普】时间单位大盘点(时间单位都有哪些?)

时间单位,是7种基本单位之一,长度、时间、质量、物质的量、光照度、电流和(热力学)温度是七种基本单位。本词条中时间单位以时间从大到小列。今天我们来盘点下时间的单位换算...

基于PHP的Laravel框架,盘点Github高星Web管理后台,效率为王!

在Web开发工作中,选择一个高效、稳定的后台管理系统是提高开发效率的关键。虽然PHP在近些年中的热度有所减退,但其上手简单、开源、灵活且被广泛应用的特点,仍然使其在编程语言排行榜中保持前十的位置。这表...

如何使用PHP编写一个简单的留言板?

留言板是一个常见的Web应用程序,允许用户在网站上发布和查看留言。在本文中,我们将使用PHP编写一个简单的留言板,介绍构建过程中的关键步骤和技巧。一、准备工作在开始编写留言板之前,我们需要准备好以下工...

产品经理提需求时要考虑的 15 个隐性需求

虽然世界充满未知的变化,但是有一些大的方向还是可以把握的,本文跟大家谈谈产品经理提需求时要考虑的15个隐性需求,enjoy~俗话说,计划赶不上变化快,无论需求文档做得如何细致,考虑得如何周全,总会...

关于 PHP 启动 MongoDb 找不到指定模块问题

前言:最近有一个小demo,需要通过PHP将用户行为记录储存到MongoDB,再用Spark做协同过滤。由于以前处理跨语言交互是通过消息中间件,这次本地使用MongoDB却弄出了几个问...

PHP程序员老鸟面试经历(php程序员怎么样)

在任何时代找任何工作都有面试这么一说的。特别是高端技术类的工种对技术理论和技术实操能力要求很严格。大部分公司招收技术员工的要求也越来愈高。至于PHP程序员也是如此,我估计大多数PHP老鸟已经不在意所...