线上踩坑:Redis集群调用Lua脚本-ERR bad lua script for redis cluster, all the keys that the script uses should

问题

-ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS arrayrn

上线遇到一个阿里云Redis集群的坑,特地写出来,供各位遇到此问题的道友参考,这是因为阿里云的Redis集群对Lua脚本调用的时候做了限制:

#Lua使用限制
为了保证脚本里面的所有操作都在相同slot进行,云数据库Redis集群版本会对Lua脚本做如下限制:

`所有key都应该由KEYS数组来传递,redis.call/pcall中调用的redis命令,key的位置必须是KEYS array(不能使用Lua变量替换KEYS),否则直接返回错误信息:`
-ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS arrayrn

排查

排查了一下写的lua脚本:

--获取KEY
local key1 = KEYS[1]

local val = redis.call('incr', key1)
local ttl = redis.call('ttl', key1)

--获取ARGV内的参数并打印
local times = ARGV[1]
local expire = ARGV[2]

redis.log(redis.LOG_DEBUG,tostring(times))
redis.log(redis.LOG_DEBUG,tostring(expire))

redis.log(redis.LOG_NOTICE, "incr "..key1.." "..val);
if val == 1 then
    redis.call('expire', key1, tonumber(expire))
else
    if ttl == -1 then
        redis.call('expire', key1, tonumber(expire))
    end
end

if val > tonumber(times) then
    return 0
end

return 1

本脚本的功能是通过Redis做集群的限流,此处不做赘述。有时间会专门写一节关于Redis实现分布式限流的文章。

解决

因为使用的是redis集群,在调用lua脚本的时候,key的位置必须是数组(不能使用Lua变量替换KEYS数组),否则直接返回错误信息。所以需要对lua脚本进行改正,去掉自定义的变量local,直接使用传入的KEYS数组。

--获取KEY
-- local key1 = KEYS[1] **去掉**

local val = redis.call('incr', KEYS[1])
local ttl = redis.call('ttl', KEYS[1])

--获取ARGV内的参数并打印
local times = ARGV[1]
local expire = ARGV[2]

redis.log(redis.LOG_DEBUG,tostring(times))
redis.log(redis.LOG_DEBUG,tostring(expire))

redis.log(redis.LOG_NOTICE, "incr "..KEYS[1].." "..val);
if val == 1 then
    redis.call('expire', KEYS[1], tonumber(expire))
else
    if ttl == -1 then
        redis.call('expire', KEYS[1], tonumber(expire))
    end
end

if val > tonumber(times) then
    return 0
end

return 1

最后,再贴一下阿里云对lua的一些限制及要求:

Lua使用限制

为了保证脚本里面的所有操作都在相同slot进行,云数据库Redis集群版本会对Lua脚本做如下限制:

  • 所有key都应该由KEYS数组来传递,

    redis.call/pcall

    中调用的redis命令,key的位置必须是KEYS array(不能使用Lua变量替换KEYS),否则直接返回错误信息:

    -ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS arrayrn
    
  • 所有key必须在一个slot上,否则返回错误信息:

    -ERR eval/evalsha command keys must be in same slotrn
    
  • 调用必须要带有key,否则直接返回错误信息:

    -ERR for redis cluster, eval/evalsha number of keys can't be negative or zerorn
    

参考地址:Lua脚本支持与限制

https://help.aliyun.com/document_detail/92942.html?spm=5176.13910061.sslink.1.36426f0dV6cOrU

### 如何在 Lua 中连接并操作 Redis #### 使用 `lua-resty-redis` 进行连接和命令执行 为了在 OpenResty 环境下高效地与 Redis 交互,推荐使用官方提供的 `lua-resty-redis` 模块。此模块不仅简化了与 Redis 数据库之间的通信过程,还提供了诸如连接池这样的高级特性以提升性能。 ```lua local redis = require "resty.redis" local red = redis:new() red:set_timeout(1000) -- 设置超时时间为1秒 -- 创建新的连接实例, 参数为主机名和端口号 local ok, err = red:connect("127.0.0.1", 6379) if not ok then ngx.say("failed to connect: ", err) return end -- 执行 SET 和 GET 命令 local res, err = red:set("greeting", "hello world") if not res then ngx.say("failed to set greeting: ", err) return end res, err = red:get("greeting") if not res then ngx.say("failed to get greeting: ", err) else ngx.say("greeting is: ", res) end -- 将当前使用的连接放回连接池中 local ok, err = red:set_keepalive(10000, 100) if not ok then ngx.say("failed to set keepalive: ", err) return end ``` 上述代码展示了如何利用 `lua-resty-redis` 来建立同 Redis 的会话,并完成基本的数据存取工作[^1]。 #### 利用 `redis.call()` 函数直接发送指令给 Redis 除了借助专门设计好的客户端库之外,在某些场景下也可以考虑采用更为底层的方式——即通过调用 `redis.call()` 函数向服务器传递具体的操作请求: ```lua -- 直接调用SET命令设置键值对 redis.call("set", "mykey", "value") -- 获取指定键对应的值 local result = redis.call("get", "mykey") ngx.print(result) ``` 这种方式适用于那些希望减少依赖项数量的应用程序开发人员;不过需要注意的是,这样做可能会丧失掉一些由高层级 API 提供的安全性和易用性保障[^2]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值