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

Redis中的Lua脚本怎么玩

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

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.

相关推荐

十年之重修Redis原理(redis重试机制)

弱小和无知并不是生存的障碍,傲慢才是。--------面试者...

Redis 中ZSET数据类型命令使用及对应场景总结

1.zadd添加元素zaddkeyscoremember...

redis总结(redis常用)

RedisTemplate封装的工具类packagehk.com.easyview.common.helper;importcom.alibaba.fastjson.JSONObject;...

配置热更新系统(如何实现热更新)

整体设计概览┌────────────┐┌────────────────┐┌────────────┐│配置后台服务│--写入-->│Red...

java高级用法之:调用本地方法的利器JNA

简介JAVA是可以调用本地方法的,官方提供的调用方式叫做JNI,全称叫做javanativeinterface。要想使用JNI,我们需要在JAVA代码中定义native方法,然后通过javah命令...

SpringBoot:如何优雅地进行响应数据封装、异常处理

背景越来越多的项目开始基于前后端分离的模式进行开发,这对后端接口的报文格式便有了一定的要求。通常,我们会采用JSON格式作为前后端交换数据格式,从而减少沟通成本等。...

Java中有了基本类型为什么还要有包装类型(封装类型)

Java中基本数据类型与包装类型有:...

java面向对象三大特性:封装、继承、多态——举例说明(转载)

概念封装:封装就是将客观的事物抽象成类,类中存在属于这个类的属性和方法。...

java 面向对象编程:封装、继承、多态

Java中的封装(Encapsulation)、继承(Inheritance)和多态(Polymorphism)是面向对象编程的三大基本概念。它们有助于提高代码的可重用性、可扩展性和可维护性。...

怎样解析java中的封装(怎样解析java中的封装文件)

1.解析java中的封装1.1以生活中的例子为例,打开电视机的时候你只需要按下开关键,电视机就会打开,我们通过这个操作我们可以去间接的对电视机里面的元器件进行亮屏和显示界面操作,具体怎么实现我们并不...

python 示例代码(python代码详解)

以下是35个python代码示例,涵盖了从基础到高级的各种应用场景。这些示例旨在帮助你学习和理解python编程的各个方面。1.Hello,World!#python...

python 进阶突破——内置模块(Standard Library)

Python提供了丰富的内置模块(StandardLibrary),无需安装即可直接使用。以下是一些常用的内置模块及其主要功能:1.文件与系统操作...

Python程序员如何调试和分析Python脚本程序?附代码实现

调试和分析Python脚本程序调试技术和分析技术在Python开发中发挥着重要作用。调试器可以设置条件断点,帮助程序员分析所有代码。而分析器可以运行程序,并提供运行时的详细信息,同时也能找出程序中的性...

python中,函数和方法异同点(python方法和函数的区别)

在Python中,函数(Function)...

Python入门基础命令详解(python基础入门教程)

以下是Python基本命令的详解指南,专为初学者设计,涵盖基础语法、常用操作和实用示例:Python基本命令详解:入门必备指南1.Python简介特点:简洁易读、跨平台、丰富的库支持...