spring data redis와 트랜잭션

최선욱·2021년 8월 3일
3

Redis는 rollback을 지원하지 않는다?

레디스 트랜잭션은 다른 DB와는 다르게 rollback을 지원하지않는다. 단지 명령어들을 하나의 큐에 넣고 큐에 쌓인 명령어들이 실행되는 동안 다른 명령어가 실행되지않음을 보장할 뿐이다. 따라서 트랜잭션 중간에 에러가 나는 경우엔 에러가 난 명령어만 실행되지않고 나머지 명령어들은 모두 수행된다.

spring data redis transaction

spring data redis 공식문서

//execute a transaction
List<Object> txResults = redisTemplate.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));

위 코드는 spring data redis 공식문서에 나와있는 트랜잭션 코드이다. 그런데 이런 궁금증이 생겼다.

레디스는 큐에 명령어를 모아 한번에 실행하는데 어플리케이션에서 레디스에 있는 데이터를 가져와 업데이트하면 어떻게 큐에 넣지?

다행히 공식문서에 설명이 잘 나와있었다.

However, RedisTemplate is not guaranteed to run all the operations in the transaction with the same connection.

Read-only commands, such as KEYS, are piped to a fresh (non-thread-bound) RedisConnection to allow reads. Write commands are queued by RedisTemplate and applied upon commit.

즉, 레디스는 트랜잭션 내에 같은 커넥션에서 수행됨을 보장하지 않고 읽기전용 명령어들은 새로운 커넥션에서 수행되며 쓰기전용 명령어들만 큐에 담는다는 얘기이다.

하지만 만약 커넥션 한번으로 여러 데이터를 조회하고 싶다면 위 예제 코드에서처럼 SessionCallback을 넣어주면 트랜잭션은 하나의 커넥션에서 모든 동작을 수행한다. 또는 spring data redis에서 지원하는 pipelining을 사용해도 된다. 트랜잭션에서 rollback에 대한 오버헤드가 없기 때문에 두개의 성능은 비슷하다고 한다.

레디스 트랜잭션이 헷갈렸던 이유

레디스는 다른 DB와 다르게 기본적으로 몇몇 명령어들을 제외하곤 싱글 쓰레드로 동작한다. 따라서 병렬성은 보장하지 못하지만 여러 요청을 빠르게 돌아가며 처리하며 동시성을 보장하려고 노력한다. 이런 싱글 쓰레드가 가진 특징때문에 레디스 트랜잭션은 one step에 트랜잭션이 한번에 처리되어야 하고 레디스 트랜잭션의 atomic 성질은 이 특징을 말하는 것 같다. 하지만 mysql이나 다른 DB에서는 트랜잭션이 끝난 뒤 상태를 commit하거나 rollback하거나를 의미하는 atomic을 말하는 것이기 때문에 헷갈렸던 것 같다.

그리고 추가적으로 transaction에서 watch 명령어 때문에 헷갈렸는데 watch 명령어는 낙관적 락을 거는 명령어이다. 즉, 단 한번에 update만 허용하며 update가 되었을 경우 다른 이후 다른 명령어나 transaction은 discard한다.

0개의 댓글