当前位置: 首页 > news >正文

Redis--Lua脚本以及在SpringBoot中的使用

前言、为什么要用 Lua?

  • 多步操作合并为一步,保证原子性。

  • 减少网络通信次数。

  • 下推逻辑到 Redis,提高性能。

一、Redis 使用 Lua 脚本的两种方式

方式一:使用 --eval 执行脚本文件

这种方式 需要先写一个 Lua 文件

📌 示例:创建一个 setname.lua 文件,内容如下:

-- KEYS[1] 表示 key
-- ARGV[1] 表示 value
-- ARGV[2] 表示过期时间(秒)
return redis.call("set", KEYS[1], ARGV[1], "EX", ARGV[2])

在命令行执行:

redis-cli --eval setname.lua name , czq 5

📌 解释:

  • --eval setname.lua namename 传给 KEYS[1]

  • 逗号 , 后面的是 ARGV"czq"ARGV[1]5ARGV[2]

执行效果:

OK

再验证:

get name
"czq"
ttl name
(integer) 5   # 有效期 5 秒

方式二:使用 eval 命令直接写脚本

这种方式 直接在 redis-cli 里执行 Lua 代码,不需要写文件。

📌 示例:

redis-cli

进入 redis-cli 后执行:

eval "return redis.call('set', KEYS[1], ARGV[1], 'EX', ARGV[2])" 1 name czq 5

📌 解释:

  • "return redis.call(...)" → 直接写 Lua 代码

  • 1 表示有 1 个 KEYS 参数

  • name → KEYS[1]

  • czq → ARGV[1]

  • 5 → ARGV[2]

结果:

OK

二者区别

  • redis-cli --eval ... 是在 Linux shell 里执行,不用手动进入交互模式。(方式一)

    • redis-cli --eval lua文件的路径/lua的名称.lua....

  • eval "..." 必须进入 redis-cli 交互模式才能用。(方式二)


三、KEYS 和 ARGV 的作用

在 Redis 的 Lua 脚本里:

  • KEYS → 存放 key(可以有多个,KEYS[1]、KEYS[2]...)

  • ARGV → 存放参数(value、过期时间等)

📌 示例:

-- 假设脚本里是这样:
return "KEY=" .. KEYS[1] .. ", VALUE=" .. ARGV[1] .. ", TTL=" .. ARGV[2]

执行:

eval "return 'KEY='..KEYS[1]..', VALUE='..ARGV[1]..', TTL='..ARGV[2]" 1 name czq 5

输出结果:

"KEY=name, VALUE=czq, TTL=5"

👉 总结:

  • KEYS 专门用来传 key(好处是 Redis 会自动进行 key hash 定位,支持集群)

  • ARGV 专门用来传其他参数(value、过期时间等)


四、Spring Boot 使用 Lua 脚本

 这个例子是SETNX + 过期时间结合成的原子性,通常用于 分布式锁一人一单 之类的业务。

在 Spring Boot 里也可以执行这个 Lua 脚本。

📌 示例代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;import java.util.Collections;@Service
public class RedisLuaService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;/*** 使用 SETNX + EX 实现键值设置(例如分布式锁)*/public Object setNxWithExpire() {// 1️⃣ Lua 脚本// 先尝试 SETNX,如果成功,再设置过期时间String lua ="if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then " +"   redis.call('expire', KEYS[1], ARGV[2]) " +"   return 1 " +"else " +"   return 0 " +"end";// 2️⃣ 封装脚本DefaultRedisScript<Long> script = new DefaultRedisScript<>();script.setScriptText(lua);script.setResultType(Long.class);// 3️⃣ 执行脚本return redisTemplate.execute(script,     Arrays.asList("lock_key"), // KEYS[1] 例如分布式锁的 key,keys 参数要求传的是一个 List<K>,定义成 List<String>,方便支持多个 key。"czq", "5" // ARGV[1] = value (锁的标识),ARGV[2] = 过期时间秒);}
}
✅ 使用说明
Object result = redisLuaService.setNameWithExpireIfAbsent();
System.out.println(result); 
  • 返回 1:设置成功(key 原本不存在)。

  • 返回 0:设置失败(key 已经存在)。

Redis 里结果:

127.0.0.1:6379> get name
"czq"
127.0.0.1:6379> ttl name
(integer) 5

📌 改造后的好处

  1. 保证原子性

    • 用 Lua 保证 SETNXEXPIRE 是在 Redis 内部一次性执行,避免 SETNX 成功但服务挂掉导致没有设置过期时间,从而出现“死锁”

  2. 分布式锁场景

    • SETNX 确保只有一个客户端能拿到锁。

    • EXPIRE 确保即使客户端崩溃,锁也会在过期时间后自动释放。

  3. 一人一单 / 防重提交

    • SETNX 用来保证某个 key(比如订单 ID 或用户 ID)只能被设置一次。

    • EXPIRE 防止 key 永久占用,给系统自动恢复的能力。

  4. 返回值可控

    • 返回 1 表示设置成功(抢到锁 / 成功下单)。

    • 返回 0 表示设置失败(别人已经抢到锁 / 已经下过单

📌小贴士:

        Redis 本身就支持 SET key value EX seconds NX 命令(SETNX + EXPIRE的原子性组合版),它是原子性的,不需要 Lua。Spring 的 RedisTemplate 里也可以直接调用,避免自己写 Lua。

@Service
public class RedisService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;public Boolean setIfAbsentWithExpire(String key, String value, long seconds) {return redisTemplate.opsForValue().setIfAbsent(key, value, seconds, TimeUnit.SECONDS);}
}
优点

        ✅ 原子性保证(内部就是单条 Redis 命令)。
✅ 使用简单,无需写 Lua。
✅ 代码更可读,Spring 已经封装好了。

http://www.dtcms.com/a/360702.html

相关文章:

  • 三、Gitee平台使用指南
  • 第 94 场周赛:叶子相似的树、模拟行走机器人、爱吃香蕉的珂珂、最长的斐波那契子序列的长度
  • Eclipse Compiler for Java (ECJ):安装指南与高效快捷键全解析
  • 构建无广告私人图书馆Reader与cpolar让电子书库随身携带
  • 鸿蒙总改变字体大小设置
  • 【Linux篇章】再续传输层协议UDP :从低可靠到极速传输的协议重生之路,揭秘无连接通信的二次进化密码!
  • 深度学习框架与工具使用心得:从入门到实战优化
  • Unity核心概念③:Inspector窗口可编辑变量
  • 电科金仓 KFS 场景化实践路径解析:从行业场景落地看技术价值转化
  • JP4-1-MyLesson项目简介
  • 掌握正则表达式与文本处理:提升 Shell 编程效率的关键技巧
  • Go 语言 sync 包解析
  • [React]监听Form中某个字段的变化
  • vue2》》Computed、Watch
  • 【Vue2 ✨】Vue2 入门之旅(四):生命周期钩子
  • Git从零到远程协作:手把手实战指南
  • C 语言进程通信之信号API
  • [线上问题排查]1.数据库死锁全解析与解决方案
  • 算法:插入排序
  • LeetCode 刷题【58. 最后一个单词的长度、59. 螺旋矩阵 II】
  • 【开题答辩全过程】以 基于SSM的拾光咖啡厅管理系统的设计与实现为例,包含答辩的问题和答案
  • Introduction to GIS —— Chapter 4(Raster Data Model)
  • 批量修改用户密码的命令chpasswd
  • FTP - 学习/实践
  • JPEG XS概述
  • 草图大师SketchUp 2025下载安装教程与胚子库插件包安装for SketchUp 2025安装教程
  • 【AI智能体】LLM记账智能体+MCP服务-实现步骤与效果展示
  • 网络流量分析——使用Wireshark进行分析
  • SW - 用装配图的方式组合多个子零件然后转换成为零件,可维护性好
  • DRF快速构建RESTful API指南