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;
    });
}


相关文章
|
存储 负载均衡 NoSQL
【赵渝强老师】Redis Cluster分布式集群
Redis Cluster是Redis的分布式存储解决方案,通过哈希槽(slot)实现数据分片,支持水平扩展,具备高可用性和负载均衡能力,适用于大规模数据场景。
975 2
|
存储 NoSQL 数据库
Redis 逻辑数据库与集群模式详解
Redis 是高性能内存键值数据库,广泛用于缓存与实时数据处理。本文深入解析 Redis 逻辑数据库与集群模式:逻辑数据库提供16个独立存储空间,适合小规模隔离;集群模式通过分布式架构支持高并发和大数据量,但仅支持 database 0。文章对比两者特性,讲解配置与实践注意事项,并探讨持久化及性能优化策略,助你根据需求选择最佳方案。
1413 5
|
NoSQL 算法 Redis
【Docker】(3)学习Docker中 镜像与容器数据卷、映射关系!手把手带你安装 MySql主从同步 和 Redis三主三从集群!并且进行主从切换与扩容操作,还有分析 哈希分区 等知识点!
Union文件系统(UnionFS)是一种**分层、轻量级并且高性能的文件系统**,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem) Union 文件系统是 Docker 镜像的基础。 镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
1216 6
|
存储 监控 NoSQL
Redis高可用架构全解析:从主从复制到集群方案
Redis高可用确保服务持续稳定,避免单点故障导致数据丢失或业务中断。通过主从复制实现数据冗余,哨兵模式支持自动故障转移,Cluster集群则提供分布式数据分片与水平扩展,三者层层递进,保障读写分离、容灾切换与大规模数据存储,构建高性能、高可靠的Redis架构体系。
|
存储 运维 NoSQL
Redis集群模式
Redis集群是一种分布式存储方案,旨在解决数据存储容量不足的问题。它通过将数据分片存储在多个节点上,实现数据的横向扩展。常见的分片算法包括哈希求余、一致性哈希和哈希槽分区。其中,Redis采用哈希槽分区算法,将数据均匀分配到16384个槽位中,每个分片负责一部分槽位。当节点故障时,集群通过故障检测和主从切换机制,确保服务的高可用性。集群还支持自动的数据迁移和负载均衡,保障系统稳定运行。
|
负载均衡 NoSQL Redis
【赵渝强老师】Redis的主从复制集群
Redis主从复制是指将一台Redis服务器的数据复制到其他Redis服务器,实现数据热备份、故障恢复、负载均衡及高可用架构的基础。主节点负责写操作,从节点同步数据并可提供读服务,提升并发处理能力。
528 5
|
存储 NoSQL 算法
Redis的集群架构与使用经验
本文介绍了Redis的集群架构与使用经验,包括主从复制、哨兵集群及Cluster分片集群的应用场景与实现原理。内容涵盖Redis主从同步机制、数据分片存储方式、事务支持及与Memcached的区别,并讨论了Redis内存用尽时的处理策略。适用于了解Redis高可用与性能优化方案。
|
存储 NoSQL Redis
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 +  无锁架构 +  EDA架构  + 异步日志 + 集群架构
|
缓存 NoSQL 搜索推荐
【📕分布式锁通关指南 03】通过Lua脚本保证redis操作的原子性
本文介绍了如何通过Lua脚本在Redis中实现分布式锁的原子性操作,避免并发问题。首先讲解了Lua脚本的基本概念及其在Redis中的使用方法,包括通过`eval`指令执行Lua脚本和通过`script load`指令缓存脚本。接着详细展示了如何用Lua脚本实现加锁、解锁及可重入锁的功能,确保同一线程可以多次获取锁而不发生死锁。最后,通过代码示例演示了如何在实际业务中调用这些Lua脚本,确保锁操作的原子性和安全性。
1101 6
【📕分布式锁通关指南 03】通过Lua脚本保证redis操作的原子性
|
缓存 NoSQL 测试技术
Redis压测脚本及持久化机制
Redis压测脚本及持久化机制简介: Redis性能压测通过`redis-benchmark`工具进行,可评估读写性能。持久化机制包括无持久化、RDB(定期快照)和AOF(操作日志),以及两者的结合。RDB适合快速备份与恢复,但可能丢失数据;AOF更安全,记录每次写操作,适合高数据安全性需求。两者结合能兼顾性能与安全性,建议同时开启并定期备份RDB文件以确保数据安全。
413 9