RedisTemplate EvalSha不支持集群模式

文章讲述了在项目开发中,使用RedisTemplate执行Lua脚本在单机环境成功,但在集群环境中因`EvalSha`不支持而引发异常。问题的原因在于RedisTemplate默认获取的是单机连接。解决方案是检查连接类型,分别针对JedisCluster和Jedis实例执行不同的脚本执行方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RedisTemplate访问单机和集群

1. 背景

项目开发中,需要执行Lua脚本。因为测试环境的Redis是单节点部署,所以在开发中使用如下代码是成功执行的:

   @Resource
   StringRedisTemplate stringRedisTemplate;
   
   public Object exec(RedisScript script, List<String>keys,Object... args){
        return stringRedisTemplate.execute(script,keys,args);
    }

2. 异常信息

但是准生产及生产环境的Redis是集群部署,所以在使用上述代码时会报错,EvalSha不支持集群环境:

org.springframework.dao.InvalidDataAccessApiUsageException: EvalSha is not supported in cluster environment.
	at org.springframework.data.redis.connection.jedis.JedisClusterScriptingCommands.evalSha(JedisClusterScriptingCommands.java:83)
	at org.springframework.data.redis.connection.DefaultedRedisConnection.evalSha(DefaultedRedisConnection.java:1240)
	at org.springframework.data.redis.connection.DefaultStringRedisConnection.evalSha(DefaultStringRedisConnection.java:1653)
	at sun.reflect.GeneratedMethodAccessor173.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.data.redis.core.CloseSuppressingInvocationHandler.invoke(CloseSuppressingInvocationHandler.java:61)
	at com.sun.proxy.$Proxy175.evalSha(Unknown Source)
	at org.springframework.data.redis.core.script.DefaultScriptExecutor.eval(DefaultScriptExecutor.java:77)
	at org.springframework.data.redis.core.script.DefaultScriptExecutor.lambda$execute$0(DefaultScriptExecutor.java:68)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:224)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:184)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:171)
	at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:58)
	at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:52)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:346)

3. 原因分析

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

调用链:
org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.script.RedisScript<T>, java.util.List<K>, java.lang.Object...)
org.springframework.data.redis.core.script.DefaultScriptExecutor#execute(org.springframework.data.redis.core.script.RedisScript<T>, java.util.List<K>, java.lang.Object...)
org.springframework.data.redis.core.script.DefaultScriptExecutor#execute(org.springframework.data.redis.core.script.RedisScript<T>, org.springframework.data.redis.serializer.RedisSerializer<?>, org.springframework.data.redis.serializer.RedisSerializer<T>, java.util.List<K>, java.lang.Object...)
org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.RedisCallback<T>)
org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.RedisCallback<T>, boolean)
org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.RedisCallback<T>, boolean, boolean)
org.springframework.data.redis.core.RedisConnectionUtils#getConnection(org.springframework.data.redis.connection.RedisConnectionFactory, boolean)
org.springframework.data.redis.core.RedisConnectionUtils#doGetConnection
org.springframework.data.redis.core.RedisConnectionUtils#fetchConnection

可以看到在 org.springframework.data.redis.core.RedisConnectionUtils#fetchConnection 方法中,如果配置的是单节点的redis,获取到的连接是单机的

4. 解决方案

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

5. JedisCluster 和 Jedis 两种连接类型的区别

JedisCluster

Jedis

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值