阿里云redis集群使用lua脚本
redis集群对lua集群的支持有限,阿里的文档描述也比较简单,没有demo,研究了好久才把单例的lua脚本修改成集群版
单例模式的lua脚本
local strs = {};
local result = {};
local resultIndex = 2;
-- ARGV[1]是操作集合的指令,这里对应的是正序还是倒序zrange、zrevrange
-- KEYS[1]是zset的key
-- ARGV[2]是从第几个元素开始
-- ARGV[3]是第几个元素结束
local list = redis.call(ARGV[1], KEYS[1], ARGV[2], ARGV[3])
if(list[1] == nil) then
return "[]"
end
result[1] = "[";
for index,v in ipairs(list) do
-- KEYS[2]是存投票项的hash的key,值是vote:%s:%s:voteItem:list:
-- 拼写主Key
local key = KEYS[2]..v
--大量拼接字符串..效率比table.concat(strs)低很多。
--Lua的字符串和Java的字符串差不多,都是不可变的,每一次进行连接操作之后,其实就产生了新的字符串,不再是原来的那个了,不断连接,就不断产生新的字符串,产生新字符串是需要复制操作,随着连接操作的不断进行着,字符串越来越大,复制操作也就越来越耗时
-- 获取hash中的name值
local id = redis.call("hget", key, "id")
local itemName = redis.call("hget", key, "itemName")
local sortIndex = redis.call("hget", key, "sortIndex")
local details = redis.call("hget", key, "details")
local videoLink = redis.call("hget", key, "videoLink")
local itemImg = redis.call("hget", key, "itemImg")
local voteNumber = redis.call("hget", key, "voteNumber")
strs[1] = "{\'id\':\'";
strs[2] = id;
strs[3] = "\',";
strs[4] = "\'itemName\':\'";
strs[5] = itemName;
strs[6] = "\',";
strs[7] = "\'sortIndex\':\'";
strs[8] = sortIndex;
strs[9] = "\',";
strs[10] = "\'details\':\'";
strs[11] = details;
strs[12] = "\',";
strs[13] = "\'videoLink\':\'";
strs[14] = videoLink;
strs[15] = "\',";
strs[16] = "\'itemImg\':\'";
strs[17] = itemImg;
strs[18] = "\',";
strs[19] = "\'voteNumber\':\'";
strs[20] = voteNumber;
strs[21] = "\'}";
local voteItemJson = table.concat(strs);
redis.log(redis.LOG_DEBUG, "voteItemJson:"..voteItemJson)
result[resultIndex] = voteItemJson;
resultIndex = resultIndex + 1;
result[resultIndex] = ",";
resultIndex = resultIndex + 1;
end
if resultIndex > 2 then
table.remove(result, resultIndex - 1);
end
result[resultIndex -1] = "]"
return table.concat(result);
这个脚本直接指向会报错
-ERR eval/evalsha command keys must in same slot
集群有一个槽的概念,不同的key根据redis的hash算法会分布在不同的槽,集群是不支持多key查询的,解决办法是使用redis的hash tag,存入的时候把要多key查询的 key用{}括起来。如{key}.1,a.{key},s:{key}:b,这样这3个key都是按照{}内的内容技术槽,就能保证存入同一个槽内。
还可能会遇到这个异常
ERR bad lua script for redis cluster, first parameter of redis.call/redis.pcall must be a single literal string
这是因为redis.call()第一个参数必须是字符串,不能是ARGV传参,我试过在lua里定义变量order,然后吧order放入redis.call,还是会报这个异常,没找到什么好的解决办法,只能把指令写入redis.call(),把正序和倒序写了两个脚本
local list = redis.call(‘zrange’, KEYS[1], ARGV[1], ARGV[2])
上面的脚本还会报一个错误
"-ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS array\r\n"
这是因为在循环内,我会拼接key,local key = KEYS[2]..v,在下面的redis.call会用到这个key,但是ali的redis集群规定,redis.call里面的key只能使用KEYS数组,所以这里修改代码
KEYS[3] = KEYS[2]..v
local id = redis.call("hget", KEYS[3], "id")
这样就能在redis集群执行这个lua脚本了
还没有评论,来说两句吧...