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

简介: 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;
    });
}


相关文章
|
7月前
|
存储 负载均衡 NoSQL
【赵渝强老师】Redis Cluster分布式集群
Redis Cluster是Redis的分布式存储解决方案,通过哈希槽(slot)实现数据分片,支持水平扩展,具备高可用性和负载均衡能力,适用于大规模数据场景。
488 2
|
5月前
|
NoSQL Java 网络安全
SpringBoot启动时连接Redis报错:ERR This instance has cluster support disabled - 如何解决?
通过以上步骤一般可以解决由于配置不匹配造成的连接错误。在调试问题时,一定要确保服务端和客户端的Redis配置保持同步一致。这能够确保SpringBoot应用顺利连接到正确配置的Redis服务,无论是单机模式还是集群模式。
491 5
|
11月前
|
NoSQL 网络协议 Redis
解决:启动Redis报错
当 Redis 启动报错“Could not create *:6379”时,可能是端口占用或配置问题。解决方法:依次运行以下命令——先通过 `redis-cli.exe` 进入命令行并执行 `shutdown` 关闭服务,再用 `exit` 退出工具,最后通过 `redis-server.exe redis.windows.conf` 重启 Redis。此操作可有效释放端口并正常启动 Redis 服务。
1091 1
|
9月前
|
缓存 NoSQL 算法
高并发秒杀系统实战(Redis+Lua分布式锁防超卖与库存扣减优化)
秒杀系统面临瞬时高并发、资源竞争和数据一致性挑战。传统方案如数据库锁或应用层锁存在性能瓶颈或分布式问题,而基于Redis的分布式锁与Lua脚本原子操作成为高效解决方案。通过Redis的`SETNX`实现分布式锁,结合Lua脚本完成库存扣减,确保操作原子性并大幅提升性能(QPS从120提升至8,200)。此外,分段库存策略、多级限流及服务降级机制进一步优化系统稳定性。最佳实践包括分层防控、黄金扣减法则与容灾设计,强调根据业务特性灵活组合技术手段以应对高并发场景。
2476 7
|
缓存 NoSQL 搜索推荐
【📕分布式锁通关指南 03】通过Lua脚本保证redis操作的原子性
本文介绍了如何通过Lua脚本在Redis中实现分布式锁的原子性操作,避免并发问题。首先讲解了Lua脚本的基本概念及其在Redis中的使用方法,包括通过`eval`指令执行Lua脚本和通过`script load`指令缓存脚本。接着详细展示了如何用Lua脚本实现加锁、解锁及可重入锁的功能,确保同一线程可以多次获取锁而不发生死锁。最后,通过代码示例演示了如何在实际业务中调用这些Lua脚本,确保锁操作的原子性和安全性。
700 6
【📕分布式锁通关指南 03】通过Lua脚本保证redis操作的原子性
|
NoSQL Redis 数据库
Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
通过本文的介绍,我们详细讲解了 Lua 脚本在 Redis 中的作用、`eval` 命令的使用方法以及 `redis.call` 和 `redis.pcall` 的区别和用法。通过合理使用 Lua 脚本,可以实现复杂的业务逻辑,确保操作的原子性,并减少网络开销,从而提高系统的性能和可靠性。
826 13
|
消息中间件 NoSQL Java
Redis系列学习文章分享---第六篇(Redis实战篇--Redis分布式锁+实现思路+误删问题+原子性+lua脚本+Redisson功能介绍+可重入锁+WatchDog机制+multiLock)
Redis系列学习文章分享---第六篇(Redis实战篇--Redis分布式锁+实现思路+误删问题+原子性+lua脚本+Redisson功能介绍+可重入锁+WatchDog机制+multiLock)
711 0
|
监控 安全
公司用什么软件监控电脑:Lua 脚本在监控软件扩展功能的应用
在企业环境中,电脑监控软件对保障信息安全、提升效率至关重要。Lua 脚本在此类软件中用于扩展功能,如收集系统信息、监控软件使用时长及文件操作,向指定服务器发送数据,支持企业管理和运营。
233 6
|
缓存 分布式计算 NoSQL
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
210 2
|
存储 JSON Ubuntu
如何使用 Lua 脚本进行更复杂的网络请求,比如 POST 请求?
如何使用 Lua 脚本进行更复杂的网络请求,比如 POST 请求?