Redis集群模式使用Lua脚本的限制

16 篇文章 0 订阅
10 篇文章 0 订阅

问题复现:

ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS array, and KEYS should not be in expression
堆栈信息:

org.springframework.dao.InvalidDataAccessApiUsageException: ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS array, and KEYS should not be in expression; nested exception is redis.clients.jedis.exceptions.JedisDataException: ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS array, and KEYS should not be in expression
at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:69)
at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:42)
at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)
at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42)
at org.springframework.data.redis.connection.jedis.JedisConnection.convertJedisAccessException(JedisConnection.java:187)

由于前段时间自己写的redis定长队列使用了lua脚本,测试好好的,线上就报错了,还好不是什么大问题。因为目前测试环境单个实例,线上环境使用的是redis集群模式,所以已经踩了不少坑
起初写的脚本如下:

private static final String LIMIT_OFFER_LUA =
            "local key = KEYS[1]" +
            "local num = tonumber(ARGV[1])" +
            "local val = ARGV[2]" +
            "if (redis.call('llen', key) >= num) then redis.call('rpop', key) end " +
            "redis.call('lpush', key, val)";

其中key和arg都用lua变量,因为好几处使用了,正常来讲申明变量没什么问题,可是阿里集群对Lua脚本进行了限制,再通过错误信息,意思为,key的位置必须为数组,不能使用脚本变量

解决办法

修改lua脚本,去掉key的local变量,改为KEYS[i]数组的形式,将数组直接传入进去
修改脚本如下:

private static final String LIMIT_OFFER_LUA =
            "local num = tonumber(ARGV[1])" +
            "local val = ARGV[2]" +
            "if (redis.call('llen', KEYS[1]) >= num) then redis.call('rpop', KEYS[1]) end " +
            "redis.call('lpush', KEYS[1], val)";

集群中Lua脚本的限制

Redis Cluster对使用Lua脚本增加了一些限制,在此基础上,Redis集群版对使用Lua脚本存在如下额外限制:

  • 小版本限制,若无法执行EVAL的相关命令,并报错ERR command eval not support for normal user时,请升级小版本后重试,具体操作请参见升级小版本。
  • 所有Key必须在一个slot上,否则报错-ERR eval/evalsha command keys must be in same slot\r\n。
    您可以通过CLUSTER KEYSLOT命令获取目标Key的哈希槽(Hash Slot)进行确认。
  • 对单个节点执行SCRIPT LOAD命令时,不保证将该Lua脚本存入至其他节点中。
  • 不支持发布订阅命令,包括PSUBSCRIBE、PUBSUB、PUBLISH、PUNSUBSCRIBE、SUBSCRIBE和UNSUBSCRIBE。
  • 不支持UNPACK函数。

代理模式(Proxy)执行Lua的额外限制

  • 所有key都应该由KEYS数组来传递,redis.call/pcall中调用的Redis命令,key的位置必须是KEYS array,且不能使用Lua变量替换KEYS,否则直接返回错误信息:
  • 调用必须要带有key,否则直接返回错误信息:
  • 不支持在MULTI、EXEC事务中使用EVAL、EVALSHA、SCRIPT系列命令。
  • 不支持在Lua中执行跨Redis节点的命令,例如KEYS、SCAN等。为了保证Lua执行的原子性,Proxy会根据KEYS参数将Lua发送到一个Redis节点执行并获取结果,从而导致该结果与全局结果不一致。

参考链接
RedisLua脚本的基本语法与使用规范_云数据库 Redis 版-阿里云帮助中心

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Redis集群模式下,可以使用Redis的EVAL命令来执行Lua脚本。在执行Lua脚本时,可以使用Redis提供的一些API来实现分片。例如,可以使用Rediscluster keyslot命令来获取一个key所在的槽位,然后根据槽位来选择对应的节点进行操作。另外,还可以使用Rediscluster nodes命令来获取集群中所有节点的信息,从而实现节点的选择和负载均衡。总之,通过合理地使用Redis提供的API,可以在Redis集群模式下实现Lua脚本的分片。 ### 回答2: 在Redis集群模式下,为了实现分片,可以使用一些技巧来确保数据在正确的分片节点上执行。以下是一种常见的方法: 1. 使用哈希函数:首先,确定每个键值对应的分片节点,可以使用一个哈希函数,将每个键的哈希值映射到具体的分片节点。例如,可以使用CRC16哈希函数来计算每个键的哈希值,然后将哈希值与分片节点的数量取模,得到对应的分片节点编号。 2. 在Lua脚本中增加分片逻辑:在Lua脚本中,可以利用哈希函数对需要操作的键进行分片。首先,使用哈希函数计算键的哈希值,然后将哈希值与分片节点的数量取模得到分片节点编号。接下来,可以使用Redis的`redis.call()`函数,将操作发送给对应的分片节点进行执行。 下面是一个示例Lua脚本,用于示范如何实现分片: ``` local key = KEYS[1] local value = ARGV[1] -- 计算键的哈希值 local hash = redis.call('CRC16', key) -- 计算分片节点编号 local shard = hash % NUM_OF_SHARDS -- 发送操作至对应的分片节点 redis.call('SELECT', shard) redis.call('SET', key, value) ``` 在使用时,需要将脚本传给Redis客户端,然后执行`EVALSHA`命令来执行Lua脚本。需要注意的是,每个分片节点都需要加载相同的Lua脚本,以确保分片逻辑一致。 总之,通过使用哈希函数计算键的哈希值,并在Lua脚本中增加分片逻辑,可以实现在Redis集群模式下的分片。这样可以确保数据在正确的分片节点上执行,从而实现分布式存储和计算。 ### 回答3: 在Redis集群模式下,可以使用Lua脚本来实现分片。 Lua脚本Redis的一种脚本编程语言,可以利用其扩展Redis的功能。对于分片,可以通过键的哈希值来实现。 首先,可以将需要分片的数据按一定规则进行哈希算法计算,然后将哈希值与集群中的节点进行比较,决定将数据存放在哪个节点上。可以使用CRC16(16位循环冗余校验码)或者其他哈希算法来计算。 接下来,在Lua脚本中可以使用Redis集群命令,例如`redis.call`或`redis.pcall`来处理数据。 在实现具体功能时,可以通过节点的IP地址或其他唯一标识来计算哈希值,然后将数据插入到哈希值对应的节点上。同时,可以使用Lua脚本中的条件判断,例如`if...then...end`来处理数据的读取、更新和删除等操作。 需要注意的是,在使用Lua脚本进行分片时,应该保证节点上的数据均匀分布,避免出现过载或负载不均的情况。可以通过调整哈希算法的参数或增加节点数量来平衡负载。 总结而言,通过使用Lua脚本,在Redis集群模式下可以实现分片。通过哈希算法计算键的哈希值,并将数据分布到对应的节点上,可以实现数据的有效管理和高效访问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值