记录go-redis使用集群时,报错:CROSSSLOT Keys in request don‘t hash to the same slot

问题描述

需求:从一个变化的列表list中取出第一条数据,list 10s更新一次,10s内不能一直取第一条,需要均衡;

bug代码:

// lua脚本
var copyIndexScript string = `
		local value = redis.call("Get", KEYS[1])
		if value == false  then
			redis.call("Set" , KEYS[1], "0")
			redis.call("Expire" , KEYS[1], KEYS[2])
			return 0
		else
			local value1 = redis.call("Incr" , KEYS[1])
			return value1
		end
	`
// lua脚本缓存hash值
var copyIndexLuaHash string
//redis初始化
func init() {
	RedisClient = redis.NewClusterClient(&redis.ClusterOptions{
		Addrs:    []string{"127.0.0.1:7000","127.0.0.1:7001","127.0.0.1:7002","127.0.0.1:7003","127.0.0.1:7004","127.0.0.1:7005"},
		Password: "", //密码	
	})

	//脚本会产生一个sha1哈希值,下次用的时候可以直接使用这个值
	copyIndexLuaHash, _ = RedisClient.ScriptLoad(copyIndexScript).Result()
}

// 获取下标值
n, err := RedisClient.EvalSha(copyIndexLuaHash, []string{id, time}).Result()

报错:CROSSSLOT Keys in request don't hash to the same slot
原因:所有key必须在1个slot上

修改为:用参数传参;

// lua脚本
var copyIndexScript string = `
		local value = redis.call("Get", ARGV[1])
		if value == false  then
			redis.call("Set" , ARGV[1], "0")
			redis.call("Expire" , ARGV[1], ARGV[2])
			return 0
		else
			local value1 = redis.call("Incr" , ARGV[1])
			return value1
		end
	`
// 获取下标值
n, err := RedisClient.EvalSha(copyIndexLuaHash,nil, []string{id, time}).Result()

报错:NOSCRIPT No matching script. Please use EVAL
原因:集群模式,在当前节点,获取缓存sha1,可能获取不到;干脆直接用eval

script load命令用于将脚本script添加到脚本缓存中,但并不立即执行这个脚本。如果给定的脚本已经在缓存里面了,那么不执行任何操作。在脚本被加入到缓存之后,通过EVALSHA命令,可以使用脚本的 SHA1 校验和来调用这个脚本。脚本可以在缓存中保留无限长的时间,直到执行SCRIPT FLUSH为止。
EVAL命令也会将脚本添加到脚本缓存中,但是它会立即执行输入的脚本。

// 获取下标值
n, err := RedisClient.Eval(copyIndexScript,nil, []string{id, time}).Result()

适配阿里云redis集群,报错:ERR for redis cluster, eval/evalsha number of keys can't be negative or zerorn

阿里云Redis集群对lua脚本限制如下: https://developer.aliyun.com/article/645851

  1. 所有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”。
  2. 所有key必须在1个slot上,否则返回错误信息,“-ERR eval/evalsha command keys must be in same slotrn”。
  3. 调用必须要带有key,否则直接返回错误信息, “-ERR for redis cluster, eval/evalsha number of keys can’t be negative or zerorn”。

解决方案:

按照KEYS,ARGV传参

// lua脚本
var copyIndexScript string = `
		local value = redis.call("Get", KEYS[1])
		if value == false  then
			redis.call("Set" , KEYS[1], "0")
			redis.call("Expire" , KEYS[1], ARGV[1])
			return 0
		else
			local value1 = redis.call("Incr" , KEYS[1])
			return value1
		end
	`
// 获取下标值
n, err := RedisClient.Eval(copyIndexScript, []string{id}, time).Result()

扩展:redis lua脚本做ip限流

ratelimiting.lua,内容如下

local times = redis.call('incr',KEYS[1])
 
if times == 1 then
 redis.call('expire',KEYS[1], ARGV[1])
end
 
if times > tonumber(ARGV[2]) then
 return 0
end
return 1

在redis客户端机器上,如何测试这个脚本呢?如下:

redis-cli --eval ratelimiting.lua rate.limiting:127.0.0.1 , 10 3
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值