redis transaction
redis의 transaction 기능을 사용하면 여러 명령어들을 한 번에 실행할 수 있습니다.
redis에서 다음 명령어를 통해 transaction 기능을 사용할 수 있습니다.
- MULTI : 트랜잭션을 시작합니다. 이후 실행하는 명령어들을 모두 큐에 저장합니다.
- EXEC : 트랜잭션 안에서 큐에 저장된 연산들을 실행하고 connection 상태를 normal로 되돌립니다.
- DISCARD : 트랜잭션 안에서 큐에 저장된 연산들을 제거하고 connection 상태를 normal로 되돌립니다. 만약 WATCH가 적용되어 있었다면 WATCH를 취소합니다.
- WATCH : key에 watch를 적용하면 EXEC 명령어는 오직 해당 key가 변경되지 않았을 때만 실행합니다.
redis transaction은 MULTI 명령어를 통해 시작하고 이후 명령어들을 모두 큐에 저장합니다.
큐에 여러 개의 연산이 저장되었을 때 EXEC을 통해 연산을 실행할 수 있고 DISCARD를 통해 롤백할 수도 있습니다.
redis transaction은 다음 두 가지를 보장합니다.
- redis transaction을 EXEC 했을 때 큐에 저장된 연산들이 순차적으로 실행되는데 이 때 다른 연산이 끼어들 수 없습니다.
- 만약 EXEC 명령어 호출 전에 연결이 끊기면 아무런 연산도 실행되지 않습니다.
만약 aof 백업 방식을 사용 중일 때 EXEC 명령어가 실행되면 모든 명령어를 aof에 기록합니다.
그런데 트랜잭션 EXEC 도중 갑자기 종료된다면 aof 파일에 일부 명령어만 남아있게 되는데 이는 redis를 재시작할 때 감지하여 오류를 발생시킵니다.
redis-check-aof 도구를 사용한다면 이러한 aof의 이러한 부분적인 트랜잭션을 제거할 수 있습니다.
transaction 에러 처리
첫 번째로 EXEC 실행되기 전에 에러가 발생한 경우입니다.
트랜잭션 도중에 일부러 잘못된 문법을 입력한 경우입니다.
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET foo 1
QUEUED
127.0.0.1:6379(TX)> asdf
(error) ERR unknown command 'asdf', with args beginning with:
127.0.0.1:6379(TX)> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
우선 트랜잭션 도중 에러가 발생했고 EXEC을 실행하면 오류가 발생하면서 큐의 명령어들이 제거됩니다.
두 번째로 EXEC 실행 도중 에러가 발생한 경우입니다.
foo라는 key에 string을 저장하고 list의 연산 중 하나인 LPOP을 적용해보겠습니다.
string에는 LPOP을 사용할 수 없기 때문에 에러가 발생합니다.
그 다음에는 var라는 key에 2를 저장해보겠습니다.
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET foo 1
QUEUED
127.0.0.1:6379(TX)> LPOP foo
QUEUED
127.0.0.1:6379(TX)> SET var 2
QUEUED
127.0.0.1:6379(TX)> EXEC
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) OK
이번에는 EXEC 실행하기 전에 에러가 발생하지 않았으나 EXEC을 실행하는 도중 에러가 발생했습니다.
우리가 일반적으로 생각하는 transaction과는 성공한 연산들이 롤백되지 않는 모습을 확인할 수 있습니다.
EXEC 도중에 에러가 발생하면 해당하는 명령어만 실행되지 않고 나머지 연산은 제대로 실행됩니다.
redis가 롤백을 지원하지 않는 이유는 롤백 자체가 redis의 성능과 단순성에 큰 영향을 주기 때문이라고 합니다.
WATCH를 사용한 낙관적 락
watch 명령어는 redis transaction에 check-and-set(CAS)를 제공하기 위해 사용합ㄴ디ㅏ.
특정 key를 watch하면 해당 key의 변경 사항을 계속 모니터링 합니다.
만약 EXEC 연산을 실행하기 전에 해당 key가 변경이 되었다면 transaction을 취소합니다.
하나의 redis client만 존재한다면 걱정이 없지만 여러 redis client가 존재한다면 watch를 통한 낙관적 락을 거는 것이 좋습니다.
한 번 두 개의 redis client를 띄워서 watch 사용법을 알아보겠습니다.
첫 번째 클라이언트의 연산입니다.
127.0.0.1:6379> WATCH foo
OK
127.0.0.1:6379> GET foo
"1"
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET foo 3
QUEUED
아직 EXEC 명령어를 입력하지 않은 상황에서 두 번째 클라이언트가 foo의 값을 5로 변경해보겠습니다.
127.0.0.1:6379> GET foo
"1"
127.0.0.1:6379> SET foo 5
OK
성공적으로 변경했습니다.
이제 첫 번째 클라이언트에서 EXEC 명령어를 실행해봅니다.
127.0.0.1:6379(TX)> EXEC
(nil)
null이 반환되면서 transaction이 실행되지 않았습니다.
이렇게 watch를 통해 낙관적 락을 적용하면 트랜잭션 도중 watch하고 있는 key의 변경이 발생하면 해당 트랜잭션은 실패하고 공유 자원 문제를 해결할 수 있습니다.
참고로 redis 6.0.9버전 이후부터는 key가 만료될 경우에도 transaction이 실패합니다.
트랜잭션 내부 명령은 단순히 큐에 저장하기 때문에 조건에 해당하지 않습니다.
'공부 > Database' 카테고리의 다른 글
[redis] node.js 환경에서 redis 분산 락 구현하기 (0) | 2025.01.10 |
---|---|
[MySQL] Procedure 사용법 (2) | 2024.09.30 |
[Database] InfluxDB 쿼리 (CLI 및 API) (0) | 2023.08.30 |
[Database] InfluxDB 개념 및 설치 (0) | 2023.08.29 |
[Database] 자기 테이프와 CD-ROM의 특성 (0) | 2022.12.22 |
댓글