Redis 트랜잭션
Intro
기존 프로젝트에 Redis + refreshToken, accessToken을 이용한 인증을 적용 중이다. 현재는 로그인 부분을 리팩토링 중인데 redis에 refreshToken값을 넣는 로직을 만드는 도중 'redis 트랜잭션은 어떻게 하지?'라는 의문이 생겼다.
그래서 이번 글에는 Redis 트랜잭션에 대해 공부한 내용을 적어보겠다.
Redis 트랜잭션
MULTI
- Redis의 트랜잭션을 시작하는 명령어
- 트랜잭션을 시작하면 Redis는 이후 커맨드는 바로 실행되지 않고 queue에 쌓이게됨
EXEC
- 정상적으로 처리되어 queue에 쌓여있는 명령어를 일괄적으로 실행
- RDBMS의 Commit과 동일
DISCARD
- queue에 쌓여있는 명령어를 일괄적으로 폐기
- RDMS의 Rollback과 동일
WATCH
- Redis에서 Lock을 담당하는 명령어
- WATCH 명령어는 낙관적 락(Optimistic Lock) 기반임
- Watch 명령어를 사용하면 이 후 UNWATCH 되기전에는 1번의 EXEC 또는 Transaction 아닌 다른 커맨드만 허용함
Redis 트랜잭션 정상 실행
MULTI 명령어를 이용하여 트랜잭션을 시작하고 이후 트랜잭션 내부에서 실행될 명령어들을 입력한다. 그렇게 되면 해당 입력들은 큐에 쌓이게 된다. 참고로 get 명령어 또한 큐에 쌓인다. 이때 명령어들의 실행에 이상이 없다면 EXEC 명령어를 이용하여 큐 내부의 명령어들을 실행하게 된다.
Redis 트랜잭션 오류의 종류 두 가지
1. 구문 오류
Redis 트랜잭션을 시작한 후 구문 오류가 있을 경우 redis는 오류응답을 발생시킨 후 큐의 모든 명령을 삭제시킨다.
2. 명령어 수행 실패
명령어 수행 실패의 경우는 다르다. 예를 들어 SET ABC 5로 값을 저장한 후, MULTI 명령어를 이용하여 트랜잭션 시작 후에 HSET ABC 2 3이라는 명령어로 저장하려 할 경우 자료구조 불일치로 오류가 발생한다. 하지만 위의 사진의 결과처럼 오류메시지와 OK가 동시에 뜬다. 이후 GET ABC 명령어를 실행해 보면 트랜잭션 내의 명령어 SET ABC 4가 실행됐음을 알 수 있다. redis 공식 문서를 살펴보니 트랜잭션 시 실행 실패된 명령어를 제외한 나머지 명령어들은 실행된다고 한다.
Redis 트랜잭션의 특징
트랜잭션 실행 시 작업의 단위 내에 구문오류에 대한 rollback은 가능하지만, 명령 실행 실패에 대한 rollback은 지원하지 않는다. 이 부분이 RDBMS의 트랜잭션 처리 방식과 다른 점이다.
redis가 rollback을 지원하지 않는 이유는 명령어 수행 실패는 프로그래밍 오류의 결과이며, 대부분 개발 단계에서 발견될 수 있는 종류의 오류이므로 Production에 발생할 일은 거의 발생하지 않는다는 부분에서 rollback을 지원하지 않는다고 한다. 그 선택으로 인해 redis는 내부적으로 단순화되고 빨라졌다.
Redis 트랜잭션 락 과정
redis의 경우 낙관적 락을 지원한다.
1. WATCH 명령어로 redis에게 해당 키를 모니터링
2. MULTI 명령어를 사용하여 트랜잭션 시작, 내부에서 해당 키 값을 변경
3. 이때 트랜잭션 외부에서 모니터링하고 있는 키의 값을 변경
4. 트랜잭션 사용 중인 사용자가 EXEC 명령어 사용하여 트랜잭션 종료시킴 → 이때에 EXEC 명령어는 실행 명령어가 담겨있는 큐를 실행하기 전에 모니터링 중인 키 값의 변경이 있는지 확인함
5. 모니터링 중인 키 값의 변경이 있었으므로 트랜잭션을 실패시킴 → 큐의 모든 실행 명령어를 삭제함
6. WATCH 명령어 이후 EXEC 명령어를 사용했으므로 모니터링 중인 모든 키의 상태를 UNWATCH로 바꿈
해당 키에 대한 모니터링은 WATCH 명령어 이후부터 EXEC 또는 DISCARD 또는 UNWATCH 명령어 이전까지 유효하다. 변경감지 후 트랜잭션 성공 유무와는 관계없이 모니터링 중이었던 모든 키의 상태는 UNWATCH 상태가 된다.
결론
redis의 트랜잭션에 대해 공부해 보니 rollback을 지원하지 않는다는 점, 낙관적 락을 지원한다는 점 등, 그리고 redis의 장점들을 생각해 보면 정말 모든 부분이 '성능'에 초점이 맞춰져있구나 싶었다. 이번 공부를 통해서 redis의 특징을 제대로 파악했다는 생각이 든다. 그리고 redis는 명령어 실행 실패에 따른 rollback을 지원하지 않으므로 아무래도 현재 진행 중인 리팩토링의 로직에는 트랜잭션을 사용할 일이 없을 것 같다. redis를 사용할 때 트랜잭션을 언제 사용하는 것이 유의미한 것인지 알게 되었다.
참고