Scripting

Redis 2.6 及更高版本通过 evalevalsha 命令支持运行 Lua 脚本。Spring Data Redis 为运行脚本提供了一个高级抽象,它处理序列化并自动使用 Redis 脚本缓存。

Redis versions 2.6 and higher provide support for running Lua scripts through the eval and evalsha commands. Spring Data Redis provides a high-level abstraction for running scripts that handles serialization and automatically uses the Redis script cache.

可以通过调用`RedisTemplate`和`ReactiveRedisTemplate`的`execute`方法来运行脚本。两者都使用可配置的`ScriptExecutor`(或`ReactiveScriptExecutor`)来运行提供的脚本。默认情况下,ScriptExecutor(或`ReactiveScriptExecutor`)负责序列化提供的键和参数,以及反序列化脚本结果。这是通过模板的键和值序列化器完成的。还有一个附加的重载,它允许你传递用于脚本参数和结果的自定义序列化器。

Scripts can be run by calling the execute methods of RedisTemplate and ReactiveRedisTemplate. Both use a configurable ScriptExecutor (or ReactiveScriptExecutor) to run the provided script. By default, the ScriptExecutor (or ReactiveScriptExecutor) takes care of serializing the provided keys and arguments and deserializing the script result. This is done through the key and value serializers of the template. There is an additional overload that lets you pass custom serializers for the script arguments and the result.

默认的`ScriptExecutor`通过检索脚本的 SHA1 并首先尝试运行`evalsha`来优化性能,如果脚本尚不存在于 Redis 脚本缓存中,则回调`eval`。

The default ScriptExecutor optimizes performance by retrieving the SHA1 of the script and attempting first to run evalsha, falling back to eval if the script is not yet present in the Redis script cache.

以下示例通过使用 Lua 脚本运行常见的“check-and-set”场景。这是 Redis 脚本的理想用例,因为它要求以原子方式运行一组命令,并且一个命令的行为受到另一命令结果的影响。

The following example runs a common “check-and-set” scenario by using a Lua script. This is an ideal use case for a Redis script, as it requires that running a set of commands atomically, and the behavior of one command is influenced by the result of another.

@Bean
public RedisScript<Boolean> script() {

  ScriptSource scriptSource = new ResourceScriptSource(new ClassPathResource("META-INF/scripts/checkandset.lua"));
  return RedisScript.of(scriptSource, Boolean.class);
}
  • Imperative

  • Reactive

public class Example {

  @Autowired
  RedisOperations<String, String> redisOperations;

  @Autowired
  RedisScript<Boolean> script;

  public boolean checkAndSet(String expectedValue, String newValue) {
    return redisOperations.execute(script, singletonList("key"), asList(expectedValue, newValue));
  }
}
public class Example {

  @Autowired
  ReactiveRedisOperations<String, String> redisOperations;

  @Autowired
  RedisScript<Boolean> script;

  public Flux<Boolean> checkAndSet(String expectedValue, String newValue) {
    return redisOperations.execute(script, singletonList("key"), asList(expectedValue, newValue));
  }
}
-- checkandset.lua
local current = redis.call('GET', KEYS[1])
if current == ARGV[1]
  then redis.call('SET', KEYS[1], ARGV[2])
  return true
end
return false

前面的代码配置了一个指向名为`checkandset.lua`文件的`RedisScript`,该文件预期返回一个布尔值。脚本`resultType`应该是`Long`、BooleanList`或反序列化的值类型之一。如果脚本返回一次性状态(具体来说是`OK),它也可以是`null`。

The preceding code configures a RedisScript pointing to a file called checkandset.lua, which is expected to return a boolean value. The script resultType should be one of Long, Boolean, List, or a deserialized value type. It can also be null if the script returns a throw-away status (specifically, OK).

在应用程序上下文中配置 DefaultRedisScript 的单个实例是理想的,以避免在每次运行脚本时重新计算脚本的 SHA1。

It is ideal to configure a single instance of DefaultRedisScript in your application context to avoid re-calculation of the script’s SHA1 on every script run.

上述的 checkAndSet 方法随后运行脚本。脚本可以在 SessionCallback 中作为事务或管道的部分来运行。有关更多信息,请参见 “Redis Transactions” 和 “Pipelining”。

The checkAndSet method above then runs the scripts. Scripts can be run within a SessionCallback as part of a transaction or pipeline. See “Redis Transactions” and “Pipelining” for more information.

Spring Data Redis 提供的脚本支持也允许您使用 Spring 任务和调度器抽象对 Redis 脚本进行计划以定期运行。有关更多详细信息,请参见 Spring Framework 文档。

The scripting support provided by Spring Data Redis also lets you schedule Redis scripts for periodic running by using the Spring Task and Scheduler abstractions. See the Spring Framework documentation for more details.