Lua脚本在redis中的使用学习
0.前言
不同于之前遇到的redisTemplate的简单set、get方法,这里是使用Redis脚本执行redis操作。
DefaultRedisScript<List> script = LuaUtils.queryByVinsScript();
List<String> keys = LuaUtils.queryByVinsKeys(vins);
redisTemplate.execute(script, keys, redisArgs);
1.使用redis脚本的原因
在一些使用redis的场景中,会经常遇到并发问题。为了控制命令的执行顺序,redis提供了脚本执行功能。
Redis 保证脚本会以原子性(atomic)的方式执行: 当某个脚本正在运行的时候,不会有其他脚本或 Redis 命令被执行。
2.案例
public static List<String> queryByVinsKeys(Set<String> vins) {
return vins.stream().map(vin -> PREFIX_LATEST + vin).collect(Collectors.toList());
}
/**
* @return 数据内容:DATA|DATA|....|DATA
*/
public static DefaultRedisScript<List> queryByVinsScript() {
String luaScript = "local datas = {} ";
luaScript += "for i = 1, #KEYS do ";
luaScript += " local data = redis.call('hget', KEYS[i], 'data') ";
luaScript += " if data ~= false then ";
luaScript += " table.insert(datas, data) ";
luaScript += " end ";
luaScript += "end ";
luaScript += "return datas ";
DefaultRedisScript<List> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(luaScript);
redisScript.setResultType(List.class);
return redisScript;
}
上面的两个方法,一个是获取指定脚本中的keys,一个是获取指定脚本script,这两个参数是执行redis脚本必须要传的参数。
还有一个参数redisArgs,是想传入脚本的key以外的参数,也可以参与脚本的执行,这个参数是选填。
- keys
这里不必详述,你要查询的存储在redis中的数据对应的keys,可以看到它的结构是List,可以传递一个或者多个key
- script
这里是要执行的脚本内容,脚本是用Lua编写的。其中每一行代码的含义如下:
-- 定义一个空表datas,用于存储查询到的数据
local datas = {}
-- 定义一个循环,循环从1开始,循环的次数是传入的keys的数量;#KEYS-获取KEYS的长度
for i = 1, #KEYS do
-- 循环体第一行,执行redis命令查询数据存储到变量data里,相当于命令:hget 第i个key 'data',i是循环的次数,'data'是要查询的字段名
-- 例如我在redis中存的是hash类型的数据。其中keys有:student1、student2、student3...,每一个key对应的value是类似这样结构 {"id":1,"name":"zhangsan","age":10}的数据,那么我要获取student1、student2、student3的name,这里第一次循环执行的命令就是 hget student1 'name'
local data = redis.call('hget', KEYS[i], 'data')
-- 循环体第二行,判断:如果变量data不等于false;~=-不等于
if data ~= false then
-- 循环体第三行,如果上面的判断成立:把变量data存到循环体外面定义的空表datas中
table.insert(datas, data)
-- 循环体第四行,结束判断。
end
-- 结束本次循环
end
--返回表datas
return datas
3.Lua语法
3-1 Lua数据类型
数据类型 | 描述 |
nil | 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。 |
boolean | 包含两个值:false和true。 |
number | 表示双精度类型的实浮点数 |
string | 字符串由一对双引号或单引号来表示 |
function | 由 C 或 Lua 编写的函数 |
userdata | 表示任意存储在变量中的C数据结构 |
thread | 表示执行的独立线路,用于执行协同程序 |
table | Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。 |
其中table就是上面的案例中用到的。
local的意思的局部变量,如果前面不加则默认为全局变量
-- 创建一个空的 table
local tbl1 = {}
-- 直接初始表
local tbl2 = {"apple", "pear", "orange", "grape"}
-- 遍历在控制台打印输出表tbl2的索引
for key, val in pairs(tbl2) do
print("Key", key)
end
-- 执行结果
Key 1
Key 2
Key 3
Key 4
Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字或者是字符串。 Lua 里表的默认初始索引一般以 1 开始
-- table_test.lua 脚本文件
a = {}
a["key"] = "value"
key = 10
a[key] = 22
a[key] = a[key] + 11
for k, v in pairs(a) do
print(k .. " : " .. v)
end
相当于表table的存储数据结构如下表所示,由值和索引组成,根据索引读和写值。
值 | “value” | 22 | … | 值n |
索引 | “key” | 10 | … | 索引n |
- 注意:通过索引获取值时,常规写法是:table[i]。当索引为字符串类型时的,也可以简化为:table.i。
> site = {}
> site["key"] = "www.runoob.com"
> print(site["key"])
www.runoob.com
> print(site.key)
www.runoob.com
3-2 Lua 变量
- 变量在使用前,需要在代码中进行声明,即创建该变量
- 如果不用local声明,则默认为全局变量。
- 未声明和未赋值的变量默认值未nil,相当于不存在。
- 删除变量的方法为:给变量赋值为nil。
-- 声明变量
a = 5 -- 全局变量
local b = 5 -- 局部变量
c,d = 1,2
-- 给变量重新赋值
a = "hello" .. "world" -- 字符串拼接语法:str ... str
b = b + 1 -- 数字类型计算语法:num + num
-- 给多个变量赋值时,当变量的个数与值的个数不匹配时的规则
a, b, c = 0, 1
print(a,b,c) --> 0 1 nil
a, b = a+1, b+1, b+2 -- value of b+2 is ignored
print(a,b) --> 1 2
a, b, c = 0
print(a,b,c) --> 0 nil nil
3-3 Lua循环
for循环语法
-- var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 "执行体"。exp3 是可选的,如果不指定,默认为1
-- 相当于 for(int var = exp1;i <= exp2;i + exp3)
for var=exp1,exp2,exp3 do
<执行体>
end
for循环例子
for i=1,f(x) do
print(i)
end
for i=10,1,-1 do
print(i)
end
for的三个表达式在循环开始前一次性求值,以后不再进行求值。比如上面的f(x)只会在循环开始前执行一次,其结果用在后面的循环中。
function f(x)
print("function")
return x*2
end
for i=1,f(5) do
print(i)
end
-- 运行结果
function
1
2
3
4
5
6
7
8
9
10
- 泛型for循环
泛型 for 循环通过一个迭代器函数来遍历所有值,类似 java 中的 foreach 语句。
Lua 编程语言中泛型 for 循环语法格式:
--打印数组a的所有值
a = {"one", "two", "three"}
for i, v in ipairs(a) do
print(i, v)
end
i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组。
days = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}
for i,v in ipairs(days) do
print(v)
end
-- 运行结果
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday