-- 局部变量
local age = 30
-- 全局变量
name = "john"
--[[
数据类型:整数、浮点数、字符串、布尔值、nil
]]
local num = 42
local str = "Hello, Lua!"
local flag = true
local empty = nil
local person = { name = "John", age = 30 }
--[[
条件语句
循环语句
]]
if age < 18 then
print("未成年")
elseif age >= 18 and age < 65 then
print("成年")
else
print("老年")
end
for i = 1, 5 do
print(i)
end
local count = 0
while count < 3 do
print("循环次数: " .. count)
count = count + 1
end
repeat
print("至少执行一次")
until count > 5
-- 表数据结构,用{}定义,包含键值对,键值对可以是任何数据类型
local person = {name = "jordan",age = 23,hobbies = {"basketball","baseball"}}
print("姓名:"..person.name)
print("年龄:"..person.age)
-- 模块化,通过require关键字加载
-- 标准库,文件操作,网络编程,正则表达式,事件处理等,通过内置模块,如io/socket等
-- 字符串处理
local text = "Lua programming"
local sub = string.sub(text,1,3)
print(sub)
-- 错误处理,使用pcall汉书来包裹可能引发异常的代码块,以捕获并处理错误,通常与assert一起使用
local success,result = pcall(function()
error("出错了!")
end)
if success then
print("执行成功")
else
print("错误信息:"..result)
end
错误返回值: Lua脚本在执行期间可能会遇到错误,例如脚本本身存在语法错误,或者在脚本中的某些操作失败。Redis执行Lua脚本后,会返回脚本的执行结果。你可以检查这个结果以查看是否有错误,通常返回值是一个特定的错误标识。例如,如果脚本执行成功,返回值通常是OK,否则会有相应的错误信息。
异常处理: 在Spring Boot应用程序中,你可以使用异常处理来捕获Redis执行脚本时可能抛出的异常。Spring Data Redis提供了一些异常类,如RedisScriptExecutionException,用于处理脚本执行期间的错误。你可以使用try-catch块来捕获这些异常并采取相应的措施,例如记录错误信息或执行备用操作。
一、缓存更新
在缓存中存储某些数据,但需要定期或基于条件更新这些数据,同时确保在更新期间不会发生并发问题
使用Lua脚本,你可以原子性地检查数据的新鲜度,如果需要更新,可以在一个原子性操作中重新计算数据并更新缓存
local cacheKey = KEYS[1] --获取缓存键
local data = redis.call('GET',cacheKey) -- 尝试从缓存中获取数据
if not data then
-- 数据不在缓存中,重新计算并设置
data = calculateData()
redis.call('SET',cacheKey,data)
end
return data
二、原子操作
需要执行多个Redis命令作为一个原子操作,确保它们在多线程或多进程环境下不会被中断
使用Lua脚本,你可以将多个命令组合成一个原子操作,如实现分布式锁、计数器、排行榜等
local key = KEYS[1] -- 获取键名
local value = ARGV[1] -- 获取参数值
local current = redis.call('GET',key) -- 获取当前值
if not current or tonumber(current) < tonumber(value) then
-- 如果当前值不存在或者新值更大,设置新值
redis.call('SET',key,value)
end
-- 考虑一个计数器的场景,多个客户端需要原子性地增加计数
local key = KEYS[1]
local increment = ARGV[1]
return redis.call('INCRBY',key,increment)
三、数据处理
需要对Redis中的数据进行复杂的处理,如统计、筛选、聚合等。
使用Lua脚本,你可以在Redis中执行复杂的数据处理,而不必将数据传输到客户端进行处理,减少网络开销
local keyPattern = ARGV[1] -- 获取键名匹配模式
local keys = redis.call('KEYS',keyPattern) -- 获取匹配的键
local result = {}
for i,key in ipairs(keys) do
local data = redis.call('GET',key) -- 获取每个键对应的数据
table.insert(result,processData(data))
end
return result
--[[
Lua脚本允许你在Redis服务器端执行复杂的数据处理。这减少了将数据传输到客户端进行处理的开销,并允许你在Redis中执行更复杂的逻辑,从而提高性能
]]
local total = 0
for _,key in ipairs(KEYS) do
local value = redis.call('GET',key)
total = total + tonumber(value)
end
return total
四、分布式锁
使用Lua脚本,你可以在Redis中执行复杂的数据处理,而不必将数据传输到客户端进行处理,减少网络开销
使用Lua脚本,你可以原子性地尝试获取锁,避免竞态条件,然后在完成后释放锁
local lockKey = KEYS[1] -- 获取锁的键名
local lockValue = ARGV[1] -- 获取锁的值
local lockTimeout = ARGV[2] --获取锁的超时时间
if redis.call('SET',lockKey,lockValue,'NX','PX',lockTimeout) then
-- 锁获取成功,执行相关操作
redis.call('DEL',lockKey)
return true
else
return false --无法获取锁
五、事务
local key1 = KEYS[1]
local key2 = KEYS[2]
local value = ARGV[1]
redis.call('SET', key1, value)
redis.call('INCRBY', key2, value)
-- 如果这里的任何一步失败,整个事务将回滚
Spring Data Redis和Lettuce(或Jedis)客户端的使用
一、添加依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>io.lettuce.coregroupId>
<artifactId>lettuce-coreartifactId>
dependency>
二、application.properties或application.yml配置Redis连接
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=yourPassword
三、创建Lua脚本
myscript.lua
local a = tonumber(ARGV[1])
local b = tonumber(ARGV[2])
return a + b
四、编写代码执行Lua脚本
使用Spring Data Redis提供的StringRedisTemplate或LettuceConnectionFactory
两种不同的示例来执行Lua脚本,一种是直接运行Lua脚本字符串,另一种是运行脚本文件。
/**
运行Lua脚本字符串
*/
@Service
public class LuaScriptService{
@Autowired
private StringRedisTemplate stringRedisTemplate;
public Integer executeLuaScriptFromString(){
String luaScript = "local a = tonumber(ARGV[1])\nlocal b = tonumber(ARGV[2])\nreturn a + b";
RedisScript<Integer> script = new DefaultRedisScript<>(luaScript,Integer.class);
String[] keys = new String[0];//通常情况下,没有KEYS部分
Object[] args = new Object[]{10,20};//传递给Lua脚本的参数
Integer result = stringRedisTemplate.execute(script,keys,args);
return result;
}
}
创建一个文件myscript.lua,然后通过类运行该文件
@Service
public class LuaScriptService{
@Autowired
private StringRedisTemplate.stringRedisTemplate;
@Autowired
private ResourceLoader resourceLoader;
public Integer executeLuaScriptFromFile(){
Resource resource = resourceLoader.getResource("classpath:mysript.lua");
String luaScript;
try{
luaScript = new String(resource.getInputStream().readAllBytes());
}catch(Exception e){
throw new RuntimeException("Unable to read Lua script file.");
}
RedisScript<Integer> script = new DefaultRedisScript<>(luaScript, Integer.class);
String[] keys = new String[0]; // 通常情况下,没有KEYS部分
Object[] args = new Object[]{10, 20}; // 传递给Lua脚本的参数
Integer result = stringRedisTemplate.execute(script, keys, args);
return result;
}
}