lua脚本的基础使用

发布时间:2026/10/27 10:19:13
lua脚本的基础使用

lua脚本可以理解为一类特殊的执行单元,类似于函数,需要我们传入对应的两类参数,一类是涉及到的key,一类是具体的参数。具体逻辑语法与常规语法有所差异。

以限购场景为例

假如某一类商品(道具卡),可以被所有人购买,需要使用积分支付,而且总数目有限,同时需要更新积分,更新库存,而且需要记录积分的消费流水(购买人,购买对象,购买前积分,消耗积分,购买后积分,时间)

Key:user:point:uid;market:product:pid;user:product:pid

params:uid:用户id;count_price:总花费

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

1.定义lua脚本

前置准备:

为了优化效率将读操作(静态数据)放在脚本外执行,需要的字段作为参数传递进去

解析pid,获取单价等信息

lua脚本内部:

1.进行简单比对,快速拒绝(库存不足,余额不足)

2.更新用户积分

3.更新库存量

4.更新用户拥有商品数目

返回最终值

-- KEYS顺序:用户积分key,商品库存key,用户持有商品key -- ARGV:扣减积分值count_price,购买数量 local pointKey = KEYS[1] local stockKey = KEYS[2] local userGoodsKey = KEYS[3] local costPoint = tonumber(ARGV[1]) local buyNum = tonumber(ARGV[2]) -- 【Lua内部实时读动态资源,不使用外部传来的库存/余额快照!】 local userPoint = tonumber(redis.call('GET', pointKey) or 0) local stock = tonumber(redis.call('GET', stockKey) or 0) -- 前置校验,不通过直接返回,不执行任何写入 if userPoint < costPoint then return {0} -- 0:积分不足 end if stock < buyNum then return {1} -- 1:库存不足 end -- 校验全部通过,开始依次写入 redis.call('DECRBY', pointKey, costPoint) redis.call('DECRBY', stockKey, buyNum) redis.call('INCRBY', userGoodsKey, buyNum) return {2} -- 2:购买成功

2.解析lua脚本

lua脚本需要转换成可执行对象

@Slf4j @Component public class LuaScriptUtils { public String loadScript(String path) { try { ClassPathResource resource = new ClassPathResource(path); try (BufferedReader reader = new BufferedReader( new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8))) { return reader.lines().collect(Collectors.joining("\n")); } } catch (Exception e) { log.error("加载Lua脚本失败: {}", path, e); throw new RuntimeException("加载Lua脚本失败: " + path, e); } } }

3.执行lua脚本

封装传递参数,传入的需要转为string形式的

String luaScript = luaScriptUtils.loadScript("/lua/lottery.lua"); DefaultRedisScript<List> script = new DefaultRedisScript<>(luaScript, List.class); List<?> execute = redisTemplate.execute(script, List.of("user:point:uid","market:product:pid","user:product:pid"), uid,String.valueOf(price)); Long code = (Long) execute.get(0); if (code == 0) { // 积分不足 } else if (code == 1) { // 库存不足 } else if (code == 2) { // 成功 }

大概是这样。还有其他的执行方式,但差不多,其次因为lua返回的类型与实际接受时可能不一致,转换时需要注意一下,lua脚本执行错误不会抛出具体的异常,出错的话,需要自己排查一下,还有一些是语法问题要注意一下。