Redis トランザクション
Redis は、multi
、exec
、discard
コマンドを通じてトランザクション (英語) のサポートを提供します。これらの操作は RedisTemplate
(Javadoc) で使用できます。ただし、RedisTemplate
では、トランザクション内のすべての操作が同じ接続で実行されることは保証されません。
Spring Data Redis は、Redis トランザクションを使用する場合など、同じ connection
を使用して複数の操作を実行する必要がある場合に使用する SessionCallback
(Javadoc) インターフェースを提供します。次の例では、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
サポート
デフォルトでは、RedisTemplate
はマネージド Spring トランザクションに参加しません。@Transactional
または TransactionTemplate
を使用するときに RedisTemplate
で Redis トランザクションを使用する場合は、setEnableTransactionSupport(true)
を設定して、各 RedisTemplate
のトランザクションサポートを明示的に有効にする必要があります。トランザクションサポートを有効にすると、RedisConnection
が ThreadLocal
に基づく現在のトランザクションにバインドされます。トランザクションがエラーなしで終了した場合、Redis トランザクションは EXEC
でコミットされます。それ以外の場合は、DISCARD
でロールバックされます。Redis トランザクションはバッチ指向です。進行中のトランザクション中に発行されたコマンドはキューに入れられ、トランザクションをコミットするときにのみ適用されます。
Spring Data Redis は、進行中のトランザクションで読み取り専用コマンドと書き込みコマンドを区別します。KEYS
などの読み取り専用コマンドは、読み取りを許可するために新しい(スレッドにバインドされていない) RedisConnection
にパイプされます。書き込みコマンドは RedisTemplate
によってキューに入れられ、コミット時に適用されます。
次の例は、トランザクション管理を構成する方法を示しています。
@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 コンテキストを構成します。 |
2 | 接続を現在のスレッドにバインドすることにより、トランザクションに参加するように RedisTemplate を構成します。 |
3 | トランザクション管理には PlatformTransactionManager が必要です。Spring Data Redis には、PlatformTransactionManager 実装は付属していません。アプリケーションが JDBC を使用していると仮定すると、Spring Data Redis は、既存のトランザクションマネージャーを使用してトランザクションに参加できます。 |
次の例はそれぞれ、使用上の制約を示しています。
// 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");