MySQL 에서도 트랜잭션을 사용하듯이 Redis 에서도 트랜잭션이 필요한 경우가 있을 것이다. Redis 에서는 어떻게 설정할 수 있는지 코드로 살펴보자.
> MULTI
OK
> INCR foo
QUEUED
> INCR bar
QUEUED
> EXEC
1) (integer) 1
2) (integer) 1
MULTI 가 실행되면 이후 명령어 실행결과는 "QUEUED"라는 문자열로 응답된다. 대기 중인 명령은 간단히 EXEC가 호출될 때 실행된다.
> SET foo 1
OK
> MULTI
OK
> INCR foo
QUEUED
> DISCARD
OK
> GET foo
"1"
DISCARD 가 실행되면 MULTI 이후에 쌓인 명령어가 실행되지 않고, 트랜잭션을 종료한다.
public ResponseEntity<?> transaction() {
redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
operations.multi(); // transaction start
operations.opsForValue().set("USER:1", "1");
operations.opsForValue().set("USER:2", "2");
return operations.exec(); // transaction end
}
});
return null;
}
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<byte[], byte[]> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setEnableTransactionSupport(true); // redis Transaction On !
return redisTemplate;
}
@transactional
public ResponseEntity<?> transaction() {
redisTemplate.set("USER:1", "1");
redisTemplate.set("USER:2", "2");
return null;
}
setEnableTransactionSupport
옵션만 추가하였다.@transactional
public ResponseEntity<?> transaction() {
var result = (String) redisTemplate.opsForValue().get("USER:1");
if (result != null) { // key 에 해당하는 데이터가 존재해도 결과값은 Null 로 나와서 이 로직은 타지 않는다.
redisTemplate.opsForValue().set("USER:1", "2");
}
redisTemplate.opsForValue().set("USER:2", "2");
return null;
}
pipeline 은 커맨드를 요청할 때마다, 서버로 전송하는 것이 아니라, 커맨드를 모아서 서버에 전송시킨다.
네트워크 오버해드를 줄이기 위한 목적으로 주로 사용된다. 하지만, 커맨드는 TCP 패킷으로 나뉘어 전송되어, 전부 전송되기 전에 오류가 발생한다면, 일부만 실행되고, 나머지는 실행되지 않는 현상이 발생한다.