RedisTemplate执行lua脚本在Redis集群模式下报错EvalSha is not supported in cluster environment.

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: RedisTemplate执行lua脚本在Redis集群模式下报错EvalSha is not supported in cluster environment.


RedisTemplate执行lua脚本在Redis集群模式下报错:EvalSha is not supported in cluster environment.

执行方法,Redis是单节点执行成功但是集群下报错

public boolean unlock(String key, String value) {
    Long result = template.execute(redisScript, Arrays.asList(key),Arrays.asList(value));
    return RELEASE_SUCCESS.equals(result);
}
org.springframework.dao.InvalidDataAccessApiUsageException: EvalSha is not supported in cluster environment.
  at org.springframework.data.redis.connection.jedis.JedisClusterScriptingCommands.evalSha(JedisClusterScriptingCommands.java:83) ~[spring-data-redis-2.1.8.RELEASE.jar:2.1.8.RELEASE]
  at org.springframework.data.redis.connection.DefaultedRedisConnection.evalSha(DefaultedRedisConnection.java:1318) ~[spring-data-redis-2.1.8.RELEASE.jar:2.1.8.RELEASE]
  at org.springframework.data.redis.connection.DefaultStringRedisConnection.evalSha(DefaultStringRedisConnection.java:1729) ~[spring-data-redis-2.1.8.RELEASE.jar:2.1.8.RELEASE]
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_181]
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_181]
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_181]
  at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_181]
  at org.springframework.data.redis.core.CloseSuppressingInvocationHandler.invoke(CloseSuppressingInvocationHandler.java:61) ~[spring-data-redis-2.1.8.RELEASE.jar:2.1.8.RELEASE]
  at com.sun.proxy.$Proxy196.evalSha(Unknown Source) ~[?:?]

原因

RedisTemplate默认情况下拿到的connection是单机的

解决

集群模式直接抛出不支持执行脚本的异常,因为只能拿到原redis的connection来执行脚本

public boolean unlock(String key, String value) {
     //使用Lua脚本:先判断是否是自己设置的锁,再执行删除
     // 使用lua脚本删除redis中匹配value的key,可以避免由于方法执行时间过长而redis锁自动过期失效的时候误删其他线程的锁
     // spring自带的执行脚本方法中,集群模式直接抛出不支持执行脚本的异常,所以只能拿到原redis的connection来执行脚本
     List<String> keys = new ArrayList<>();
     keys.add(key);
     List<String> args = new ArrayList<>();
     args.add(value);
     Long result = template.execute(new RedisCallback<Long>() {
         @Override
         public Long doInRedis(RedisConnection connection) throws DataAccessException {
             Object nativeConnection = connection.getNativeConnection();
             // 集群模式和单机模式虽然执行脚本的方法一样,但是没有共同的接口,所以只能分开执行
             // 集群模式
             if (nativeConnection instanceof JedisCluster) {
                 return (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, args);
             }
             // 单机模式
             else if (nativeConnection instanceof Jedis) {
                 return (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, args);
             }
             return 0L;
         }
     });
     //返回最终结果
     return RELEASE_SUCCESS.equals(result);
 }

@Resource
StringRedisTemplate stringRedisTemplate;
public Object exec(RedisScript script, List<String>keys,Object... args){
// 将可变参转为 List
    List<String> list = new ArrayList<>();
    for (Object arg : args) {
        list.add(arg.toString());
    }
    // 将 RedisScript 转换为 string 类型的脚本
    String scriptScriptAsString = script.getScriptAsString();
    return stringRedisTemplate.execute((RedisCallback<Object>) connection -> {
// 获取连接
        Object nativeConnection = connection.getNativeConnection();
        // 集群模式的实例
        if (nativeConnection instanceof JedisCluster) {
            return (Long)((JedisCluster) nativeConnection).eval(scriptScriptAsString, keys, list);
        }
        // 单机模式的实例
        if (nativeConnection instanceof Jedis) {
            return (Long)((Jedis) nativeConnection).eval(scriptScriptAsString, keys, list);
        }
        return null;
    });
}


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
5月前
|
NoSQL Redis
Redis 执行 Lua保证原子性原理
Redis 执行 Lua 保证原子性原理
500 1
|
1月前
|
监控 安全
公司用什么软件监控电脑:Lua 脚本在监控软件扩展功能的应用
在企业环境中,电脑监控软件对保障信息安全、提升效率至关重要。Lua 脚本在此类软件中用于扩展功能,如收集系统信息、监控软件使用时长及文件操作,向指定服务器发送数据,支持企业管理和运营。
45 6
|
3月前
|
缓存 NoSQL Java
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
88 3
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
|
3月前
|
缓存 分布式计算 NoSQL
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
45 2
|
3月前
|
NoSQL Java 关系型数据库
阿里 P7二面:Redis 执行 Lua,到底能不能保证原子性?
Redis 和 Lua,两个看似风流马不相及的技术点,为何能产生“爱”的火花,成为工作开发中的黄金搭档?技术面试中更是高频出现,Redis 执行 Lua 到底能不能保证原子性?今天就来聊一聊。 
136 1
|
4月前
|
存储 JSON Ubuntu
如何使用 Lua 脚本进行更复杂的网络请求,比如 POST 请求?
如何使用 Lua 脚本进行更复杂的网络请求,比如 POST 请求?
|
8月前
|
存储 NoSQL Redis
Redis的Lua脚本有什么作用?
Redis Lua脚本用于减少网络开销、实现原子操作及扩展指令集。它能合并操作降低网络延迟,保证原子性,替代不支持回滚的事务。通过脚本,代码复用率提高,且可自定义指令,如实现分布式锁,增强Redis功能和灵活性。
293 1
|
7月前
|
消息中间件 NoSQL Java
Redis系列学习文章分享---第六篇(Redis实战篇--Redis分布式锁+实现思路+误删问题+原子性+lua脚本+Redisson功能介绍+可重入锁+WatchDog机制+multiLock)
Redis系列学习文章分享---第六篇(Redis实战篇--Redis分布式锁+实现思路+误删问题+原子性+lua脚本+Redisson功能介绍+可重入锁+WatchDog机制+multiLock)
273 0
|
5月前
|
存储 NoSQL Redis
Tair的发展问题之在Redis集群模式下,Lua脚本操作key面临什么问题,如何解决
Tair的发展问题之在Redis集群模式下,Lua脚本操作key面临什么问题,如何解决
|
7月前
|
JSON 监控 数据格式
使用Lua代码扩展上网行为管理软件的脚本功能
本文介绍了如何使用Lua脚本增强上网行为管理,包括过滤URL、记录用户访问日志、控制带宽和自动提交监控数据到网站。Lua是一种轻量级语言,适合编写扩展脚本。文中提供多个示例代码,如URL过滤器、用户活动日志记录器和带宽控制器,帮助用户根据需求定制网络管理功能。通过这些示例,用户可以快速掌握Lua在上网行为管理中的应用。
198 4