问题分析
redis.clients.jedis.exceptions.JedisNoScriptException
异常通常发生在尝试在 Redis 中执行 Lua 脚本时,但 Redis 服务器并没有加载该脚本,或者客户端尝试使用不存在的脚本 ID 来执行脚本。Jedis 客户端在执行 Lua 脚本时,通常会先将脚本加载到 Redis 服务器上,并获取一个唯一的脚本 ID,之后使用这个 ID 来执行脚本,以提高性能。如果在尝试执行一个脚本时未找到对应的 ID,就会抛出 JedisNoScriptException
异常。
报错原因
- 脚本未加载:在尝试执行 Lua 脚本之前,没有先将其加载到 Redis 服务器上。
- 脚本 ID 错误:可能使用了错误的脚本 ID 来执行脚本。
- Redis 服务器重启:如果 Redis 服务器在加载脚本后重启了,所有之前加载的脚本及其 ID 都会被清除,再次尝试使用旧的 ID 执行脚本会失败。
解决思路
- 确保脚本已加载:在执行 Lua 脚本之前,确保已使用
EVALSHA
或 Jedis 的scriptLoad
方法将脚本加载到 Redis 服务器上。 - 使用正确的脚本 ID:确保在执行脚本时使用了正确的脚本 ID。
- 处理 Redis 重启:如果 Redis 服务器可能会重启,确保在重启后重新加载脚本并获取新的 ID。
解决方法
示例代码
首先,加载 Lua 脚本到 Redis 服务器,并获取脚本 ID:
String luaScript = "return redis.call('set', KEYS[1], ARGV[1])";
String sha1 = jedis.scriptLoad(luaScript);
然后,使用获取到的脚本 ID 来执行脚本:
List<String> keys = Arrays.asList("mykey");
List<String> args = Arrays.asList("myvalue");
Object result = jedis.evalsha(sha1, keys, args);
处理 Redis 重启
如果 Redis 服务器可能会重启,你可以将脚本内容保存在客户端,并在每次尝试执行脚本之前检查是否已经加载了脚本。如果没有加载,就重新加载并获取新的 ID。这可以通过将脚本内容和对应的 ID 存储在缓存或数据库中来实现。
完整示例(包含 Redis 重启处理)
下滑查看解决方法
以下是一个简单的示例,展示了如何在 Redis 重启后重新加载 Lua 脚本:
// 假设你有一个方法来加载和获取脚本 ID
String loadScriptAndGetSha1(Jedis jedis, String luaScript) {
String sha1 = jedis.scriptExists(luaScript).stream()
.findFirst()
.orElseGet(() -> {
// 脚本不存在,加载它
return jedis.scriptLoad(luaScript);
});
return sha1;
}
// ...
// 当需要执行脚本时
String luaScript = "return redis.call('set', KEYS[1], ARGV[1])";
String sha1 = loadScriptAndGetSha1(jedis, luaScript);
List<String> keys = Arrays.asList("mykey");
List<String> args = Arrays.asList("myvalue");
Object result = jedis.evalsha(sha1, keys, args);
注意:在上面的 loadScriptAndGetSha1
方法中,我们使用了 jedis.scriptExists
来检查脚本是否已经存在。但实际上,scriptExists
方法用于检查多个脚本是否存在,并返回一个列表,其中每个元素是一个整数,表示对应位置的脚本是否存在(存在为 1,不存在为 0)。为了简化示例,我们只检查了列表中的第一个元素(即我们关心的脚本)。在真实场景中,你可能需要根据你的具体需求来处理多个脚本的情况。