Redis Transactions

Redis 通过 multiexecdiscard 命令提供对 transactions 的支持。这些操作在 RedisTemplate 中可用。但是,RedisTemplate 不能保证使用相同的连接运行事务中的所有操作。 Spring Data Redis 提供 SessionCallback 接口,可在需要使用相同的 connection 执行多个操作时使用,例如在使用 Redis 事务时。以下示例使用 multi 方法:

//execute a transaction
List<Object> txResults = redisOperations.execute(new SessionCallback<List<Object>>() {
  public List<Object> execute(RedisOperations operations) throws DataAccessException {
    operations.multi();
    operations.opsForSet().add("key", "value1");

    // This will contain the results of all operations in the transaction
    return operations.exec();
  }
});
System.out.println("Number of items added to set: " + txResults.get(0));

在返回之前,RedisTemplate 使用其值、哈希键和哈希值序列化器反序列化 exec 的所有结果。还有一个其他 exec 方法,允许你为事务结果传递自定义序列化器。 值得一提的是,如果在 multi()exec() 之间发生异常(例如在 Redis 未在超时内做出响应时发生超时异常),则连接可能会陷入事务状态。为了防止这种情况,需要丢弃事务状态以清除连接:

List<Object> txResults = redisOperations.execute(new SessionCallback<List<Object>>() {
  public List<Object> execute(RedisOperations operations) throws DataAccessException {
    boolean transactionStateIsActive = true;
	try {
      operations.multi();
      operations.opsForSet().add("key", "value1");

      // This will contain the results of all operations in the transaction
      return operations.exec();
    } catch (RuntimeException e) {
	    operations.discard();
		throw e;
    }
  }
});

@Transactional Support

默认情况下,RedisTemplate 不参与托管的 Spring 事务。如果你希望 RedisTemplate 在使用 @TransactionalTransactionTemplate 时使用 Redis 事务,则需要通过设置 setEnableTransactionSupport(true) 明确地启用每个 RedisTemplate 的事务支持。启用事务支持会将 RedisConnection 绑定到由 ThreadLocal 支持的当前事务。如果事务顺利完成,Redis 事务将使用 EXEC 提交,否则将使用 DISCARD 回滚。Redis 事务是面向批处理的。在进行事务期间发出的命令将被排队,仅在提交事务时应用。

Spring Data Redis 区分正在进行的事务中的只读和写命令。只读命令(如 KEYS)被传送到新的(非线程绑定的)RedisConnection 以允许读取。写命令由 RedisTemplate 排队并应用于提交。

以下示例展示如何配置事务管理:

Example 1. Configuration enabling Transaction Management
@Configuration
@EnableTransactionManagement                                 1
public class RedisTxContextConfiguration {

  @Bean
  public StringRedisTemplate redisTemplate() {
    StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory());
    // explicitly enable transaction support
    template.setEnableTransactionSupport(true);              2
    return template;
  }

  @Bean
  public RedisConnectionFactory redisConnectionFactory() {
    // jedis || Lettuce
  }

  @Bean
  public PlatformTransactionManager transactionManager() throws SQLException {
    return new DataSourceTransactionManager(dataSource());   3
  }

  @Bean
  public DataSource dataSource() throws SQLException {
    // ...
  }
}
1 配置 Spring 上下文以启用 {spring-framework-docs}/data-access.html#transaction-declarative[声明式事务管理]。
2 配置 RedisTemplate 通过将连接绑定到当前线程来参与事务。
3 事务管理需要 PlatformTransactionManager。Spring Data Redis 不附带 PlatformTransactionManager 实现。假设你的应用程序使用 JDBC,则 Spring Data Redis 可以通过使用现有的事务管理器参与事务。

以下每个示例都演示了一个使用限制:

Example 2. Usage Constraints
// must be performed on thread-bound connection
template.opsForValue().set("thing1", "thing2");

// read operation must be run on a free (not transaction-aware) connection
template.keys("*");

// returns null as values set within a transaction are not visible
template.opsForValue().get("thing1");