1. HTTP 트랜잭션
HTTP 트랜잭션은 클라이언트와 웹 서버 간의 통신을 나타내며, 다음과 같은 흐름으로 이루어집니다:
- 클라이언트가 HTTP 요청을 전송: 클라이언트는 서버에 요청을 보냅니다. 이 요청에는 URL, 메소드(GET, POST 등), 그리고 추가적인 데이터(예: 헤더, 본문)가 포함됩니다.
- 서버가 응답: 서버는 요청을 처리한 후, 해당하는 응답(HTTP 응답 코드, 데이터)을 클라이언트로 전송합니다.
HTTP 트랜잭션은 주로 요청-응답 기반으로 이루어지며, 요청의 성공 또는 실패에 대한 즉각적인 응답이 제공됩니다. 그러나 트랜잭션의 일관성이나 원자성에 대한 보장은 없습니다.
2. 데이터베이스 트랜잭션 (DB 트랜잭션)
데이터베이스 트랜잭션은 주로 ACID 속성에 기반하여 정의됩니다:
- 원자성(Atomicity): 트랜잭션 내의 모든 작업이 성공하거나, 하나라도 실패하면 전체 트랜잭션이 취소됩니다.
- 일관성(Consistency): 트랜잭션이 성공적으로 완료되면, 데이터베이스는 일관된 상태를 유지해야 합니다.
- 격리성(Isolation): 동시에 수행되는 여러 트랜잭션이 서로에게 영향을 미치지 않아야 합니다.
- 지속성(Durability): 트랜잭션이 완료되면 그 결과가 영구적으로 기록되어야 합니다.
데이터베이스 트랜잭션은 주로 SQL 쿼리의 형태로 진행되며, 클라이언트가 데이터베이스에 쿼리를 전송하면 데이터베이스는 이를 처리하고 결과를 반환합니다. DB 트랜잭션은 복잡한 데이터 일관성을 유지해야 할 때 유용합니다.
3. Redis 트랜잭션
Redis 트랜잭션은 특정 명령들을 묶어서 실행할 수 있는 기능을 제공합니다. Redis의 트랜잭션은 몇 가지 중요한 특징이 있습니다:
- MULTI: 트랜잭션을 시작할 때 사용하며, 이후 실행할 명령들을 큐에 저장합니다.
- EXEC: 큐에 저장된 명령들을 실행합니다.
- DISCARD: 트랜잭션 내의 명령들을 모두 취소합니다.
- WATCH: 특정 키를 감시하여, 해당 키의 값이 다른 클라이언트에 의해 수정되었을 경우 트랜잭션이 자동으로 실패하게 만듭니다.
Redis의 트랜잭션은 다음과 같은 특징을 가집니다:
- 원자성 보장: 트랜잭션 내의 명령들은 모두 하나의 블록으로 실행되며, 중간에 실패가 발생하지 않으면 한꺼번에 적용됩니다.
- 단일 스레드 처리: Redis는 단일 스레드 기반으로 동작하므로, 각 클라이언트가 명령을 보내면 순차적으로 처리됩니다. 이는 격리성을 자동으로 제공하는 장점이 있습니다.
- 에러 처리: 트랜잭션 내에서 하나의 명령이 오류를 발생시키면, 전체 트랜잭션이 실패하고 이전 명령들도 적용되지 않습니다. 이를 통해 데이터의 일관성을 유지합니다.
Redis 트랜잭션은 SQL 데이터베이스의 트랜잭션처럼 복잡한 ACID 속성을 모두 보장하지는 않지만, 빠른 응답과 기본적인 트랜잭션 처리를 제공합니다. 특히 WATCH를 사용하여 낙관적 잠금을 구현할 수 있어 특정 상황에서는 충돌 제어에 유리한 장점이 있습니다.
트랜잭션 비교 : HTTP, RDBMS , Redis
특징 | HTTP 트랜잭션 | RDBMS 트랜잭션 | Redis 트랜잭션 |
---|---|---|---|
트랜잭션 정의 | 클라이언트와 서버 간의 요청-응답 한 쌍 | 여러 SQL 명령을 하나의 작업 단위로 묶음 | 여러 Redis 명령어를 하나의 작업으로 묶음 |
원자성 (Atomicity) | 없음 | 지원 (ACID 원칙 중 하나) | 명령어 단위로 원자성 보장 |
일관성 (Consistency) | 서버 상태와 요청 간 일치성을 보장하지 않음 | 지원 (데이터 일관성 보장) | 명령어 실행 순서를 통해 일관성 보장 |
격리성 (Isolation) | 없음 | 지원 (ACID 원칙 중 하나) | 단일 스레드 처리로 자연스러운 격리성 제공 |
지속성 (Durability) | 요청이 끝나면 연결이 종료됨 | 지원 (완료된 트랜잭션은 영구적으로 저장) | 명령어 실행 후 변경사항은 영구 저장 가능 |
에러 처리 | 클라이언트에 에러 반환 | 오류 발생 시 트랜잭션 전체를 롤백 | 개별 명령어 에러 발생 시 트랜잭션 전체 취소 |
처리 방식 | 비동기적으로 처리 가능 | 동기/비동기 모두 가능 | 단일 스레드로 처리 |
주요 사용 사례 | 웹 브라우저와 서버 간의 데이터 전송 | 복잡한 데이터 처리, 높은 데이터 무결성 요구 | 빠른 캐싱 및 단순한 데이터 트랜잭션 처리 |
속도 | 빠름 (단일 요청에 대한 처리) | 상대적으로 느림 (트랜잭션 복잡도에 따라 다름) | 매우 빠름 (단일 스레드, 메모리 기반) |
트랜잭션의 복잡도 | 낮음 | 복잡 (여러 명령과 데이터 관계 관리) | 중간 (기본적인 트랜잭션 처리 가능) |
트랜잭션 관리 | 없음 | ACID 원칙에 따른 엄격한 관리 | MULTI/EXEC/DISCARD를 통한 기본 관리 |
주요 기능 | 요청-응답 기반 데이터 전송 | 복잡한 쿼리와 트랜잭션 처리 | 명령어 그룹을 원자성 있게 실행 |
요약:
- HTTP 트랜잭션은 요청-응답 간의 단순 통신 방식이며, 트랜잭션 일관성이나 원자성에 대한 요구가 없습니다.
- RDBMS 트랜잭션은 ACID 원칙을 기반으로 하며, 데이터의 무결성과 일관성을 보장하기 위한 복잡한 트랜잭션 처리가 가능합니다.
- Redis 트랜잭션은 단순하고 빠르게 트랜잭션을 처리할 수 있으며, 주로 캐싱이나 상태 관리와 같은 간단한 트랜잭션에 적합합니다.
이 표를 통해 각 트랜잭션 방식의 특성과 차이점을 명확히 비교할 수 있습니다.
REDIS 트랜잭션의 원자성
다음은 Redis 트랜잭션에서의 오류 처리 방식을 Mermaid 다이어그램을 사용하여 표현한 코드입니다. 이를 통해 트랜잭션의 전체 롤백과 부분 실행 상황을 시각적으로 나타낼 수 있습니다.
1. 전체 롤백 - 존재하지 않는 명령어로 인한 오류
2. 부분 실행 - 인자 값 오류로 인한 부분 실패
설명:
- 첫 번째 시나리오: 트랜잭션 중
SET123 key2 2000
명령에서 존재하지 않는 명령어로 인해 오류가 발생하고, 전체 트랜잭션이 EXECABORT로 롤백됩니다. - 두 번째 시나리오:
SET key2 2000 5000
에서 인자 값 오류가 발생했지만, 나머지 명령어는 정상적으로 처리됩니다. 첫 번째 명령(GET key1
)은 성공하고, 두 번째 명령에서 오류가 발생한 후, 마지막 명령(SET key3 3000
)은 정상적으로 처리됩니다.
Redis 명령어 실습
아래는 Redis의 트랜잭션을 실습하는 예시로, MULTI
, EXEC
, DISCARD
, WATCH
를 이용한 트랜잭션을 설명하는 코드 및 명령어 예시입니다. 각 기능을 Redis CLI를 사용해 시뮬레이션할 수 있습니다.
1. MULTI - EXEC/DISCARD 트랜잭션 예시
MULTI
를 사용하여 트랜잭션을 시작하고, 명령어를 큐에 저장한 후EXEC
를 통해 실행합니다.DISCARD
를 사용하여 트랜잭션을 취소할 수도 있습니다.
127.0.0.1:6379> MULTI # 트랜잭션 시작
OK
127.0.0.1:6379> SET key1 "value1" # 첫 번째 명령어 큐에 저장
QUEUED
127.0.0.1:6379> SET key2 "value2" # 두 번째 명령어 큐에 저장
QUEUED
127.0.0.1:6379> GET key1 # 세 번째 명령어 큐에 저장
QUEUED
# EXEC를 사용하여 트랜잭션 실행
127.0.0.1:6379> EXEC
1) OK # 첫 번째 SET 명령어 실행 결과
2) OK # 두 번째 SET 명령어 실행 결과
3) "value1" # GET 명령어 실행 결과
# 트랜잭션을 실행하지 않고 취소할 경우 (DISCARD 사용)
127.0.0.1:6379> MULTI # 트랜잭션 시작
OK
127.0.0.1:6379> SET key3 "value3" # 명령어 큐에 저장
QUEUED
127.0.0.1:6379> DISCARD # 트랜잭션 취소
OK
127.0.0.1:6379> GET key3 # 확인: 트랜잭션이 취소되었으므로 key3는 존재하지 않음
(nil)
2. 트랜잭션 격리성 (Isolation)
Redis 트랜잭션은 기본적으로 격리성을 제공합니다. 한 클라이언트가 트랜잭션을 실행 중일 때, 다른 클라이언트의 작업이 트랜잭션에 영향을 미치지 않습니다. 즉, 모든 명령어는 트랜잭션이 끝날 때 실행되므로, 그 전까지는 다른 명령어가 해당 키에 접근할 수 없습니다.
예시:
# 클라이언트 1
127.0.0.1:6379> MULTI # 트랜잭션 시작
OK
127.0.0.1:6379> SET key1 "value1"
QUEUED
127.0.0.1:6379> GET key1
QUEUED
# 클라이언트 2에서 GET key1 시도
127.0.0.1:6379> GET key1 # 아직 트랜잭션이 완료되지 않았으므로 값이 없음
(nil)
# 클라이언트 1에서 트랜잭션 완료
127.0.0.1:6379> EXEC
1) OK
2) "value1"
# 클라이언트 2에서 다시 GET 시도
127.0.0.1:6379> GET key1
"value1" # 트랜잭션이 완료된 후 값을 읽을 수 있음
3. WATCH with MULTI (낙관적 잠금 예시)
WATCH
명령어는 특정 키를 모니터링하여, 다른 클라이언트가 그 키를 수정하면 트랜잭션을 중단하게 만듭니다. 이 방식은 낙관적 잠금(Optimistic Locking)이라고 합니다.
예시:
# 클라이언트 1에서 WATCH 명령 실행
127.0.0.1:6379> WATCH key1
OK
127.0.0.1:6379> GET key1
"value1" # 현재 key1의 값은 "value1"
127.0.0.1:6379> MULTI # 트랜잭션 시작
OK
127.0.0.1:6379> SET key1 "new_value1"
QUEUED
# 클라이언트 2에서 key1을 수정
127.0.0.1:6379> SET key1 "other_value"
OK
# 클라이언트 1에서 트랜잭션 실행
127.0.0.1:6379> EXEC # WATCH로 인해 중단됨
(nil) # 트랜잭션 실패
127.0.0.1:6379> GET key1 # 클라이언트 2의 변경이 적용된 상태
"other_value"
설명:
- WATCH는
key1
을 모니터링하고 있습니다. 클라이언트 1이 트랜잭션을 실행하려고 하지만, 클라이언트 2가 중간에key1
값을 수정했기 때문에 트랜잭션이 중단됩니다. - 트랜잭션이 실패할 경우,
EXEC
는(nil)
을 반환하고, 트랜잭션 내의 명령어는 실행되지 않습니다.
Redis 트랜잭션 실습 요약:
1. MULTI - EXEC/DISCARD:
MULTI
로 트랜잭션 시작 후, 명령을 큐에 적재하고EXEC
로 실행하거나DISCARD
로 취소할 수 있습니다.
2. 격리성:
- Redis는 트랜잭션 내에서 명령어가 실행되기 전까지 명령어의 영향을 받지 않으며, 트랜잭션이 끝나야 키의 값이 다른 클라이언트에게 노출됩니다.
3. WATCH with MULTI:
WATCH
는 낙관적 잠금을 구현하여, 다른 클라이언트가 키를 변경할 경우 트랜잭션이 중단되도록 할 수 있습니다.EXEC
실행 전 감시 중인 키가 변경되면 트랜잭션이 실패합니다.
이 예시를 통해 Redis의 트랜잭션과 관련된 기능들을 연습할 수 있습니다.
'DB > Redis' 카테고리의 다른 글
Redis Cache 활용법: 성능 최적화를 위한 캐시 전략 (0) | 2024.09.12 |
---|---|
Redis Key와 Scan 명령어: 대규모 데이터 검색 (0) | 2024.09.11 |
Redis Bitmap 실습: 비트 단위 데이터 처리 (0) | 2024.09.11 |
Redis Geospatial 실습: 좌표 기반 데이터 처리 (0) | 2024.09.11 |
Redis Sorted Set 실습: 정렬된 데이터를 효율적으로 처리하기 (0) | 2024.09.11 |