Redis vs DB

이지예·2026년 5월 13일

개발

목록 보기
11/58

Redis vs DB — 트랜잭션, ACID, Lock?


먼저 ACID, Lock 이 뭐냐 -> (사용할 기술 선택 시 알아야하는 개념임.)

1. ACID(트랜잭션이 반드시 보장해야 할 4가지 원칙)

1-1. Atomicity (원자성)

  • 트랜잭션 내 작업은 전부 성공 or 전부 실패, 중간 상태 없음

  • 내부 구현: Undo Log — 작업 전 상태를 로그에 기록, 실패 시 해당 로그로 롤백

1-2. Consistency (일관성)

  • 트랜잭션 전후로 DB가 정의된 규칙(제약 조건, 외래키, 유니크 등)을 항상 만족
  • DB 제약 조건 + 애플리케이션 로직이 함께 보장하는 속성
  • 제약을 깨는 결과를 만드는 트랜잭션은 아예 거부됨

1-3. Isolation (격리성)

  • 동시 실행 중인 트랜잭션이 서로의 중간 상태를 볼 수 없어야 함
  • 격리성이 없을 때 발생하는 이상 현상
    • Dirty Read: 커밋되지 않은 중간값을 다른 트랜잭션이 읽음
    • Non-Repeatable Read: 같은 쿼리를 두 번 실행했을 때 결과가 다름 (중간에 다른 트랜잭션이 수정/커밋)
    • Phantom Read: 같은 범위 쿼리를 두 번 실행했을 때 행의 수가 다름 (중간에 다른 트랜잭션이 삽입/삭제)
  • 격리 수준 4단계 (높을수록 안전, 느림)
    • READ UNCOMMITTED: 커밋 안 된 데이터도 읽음. Dirty Read 발생 가능
    • READ COMMITTED: 커밋된 데이터만 읽음. Dirty Read 방지. 대부분의

DB 기본값

  • REPEATABLE READ: 같은 트랜잭션 내 동일 쿼리는 항상 같은 결과.

MySQL InnoDB 기본값

  • SERIALIZABLE: 트랜잭션을 완전히 순차 실행처럼 처리. 가장 강한 격리, 가장 느림
  • 격리 수준이 높을수록 Lock을 더 많이, 더 오래 점유 → 성능 저하

1-4. Durability (지속성)

  • 커밋된 트랜잭션은 장애(정전, 크래시)가 발생해도 데이터가 사라지지 않음

  • 내부 구현: WAL(Write-Ahead Log)

    • 실제 데이터 파일 수정 전, 반드시 로그 파일에 먼저 기록
    • 서버 재시작 시 이 로그를 보고 복구

2. Lock

2-1. Lock이 필요한 이유

  • Isolation(격리성)을 실제로 구현하는 메커니즘
  • 동시에 여러 트랜잭션이 같은 데이터에 접근할 때 충돌 방지

2-2. 비관적 락 vs 낙관적 락

  • 비관적 락 (Pessimistic Lock)
    • "충돌이 날 거라 가정하고 미리 잠근다"
    • 대표: SELECT FOR UPDATE — 읽는 시점에 X-Lock 선점
    • 안전하나 대기 시간 증가 가능
  • 낙관적 락 (Optimistic Lock)
    • "충돌이 드물다고 가정하고, 충돌 감지 시 처리한다"
    • 버전 컬럼을 두고 UPDATE 시 버전 일치 여부 확인
    • 버전이 다르면(중간에 다른 트랜잭션이 수정) 실패 처리 후 재시도
    • 충돌이 드문 환경에서 성능 유리

2-3. Lock 범위

  • Row-level Lock: 특정 행만 잠금. 동시성 높으나 관리 비용 큼
  • Table-level Lock: 테이블 전체 잠금. 관리 단순하나 동시성 저하
  • Gap Lock: 존재하지 않는 범위에 대한 잠금. Phantom Read 방지 목적

2-4. Deadlock (교착 상태)

  • 두 트랜잭션(여러 작업을 하나로 묶어서 전부 성공 or 실패)이 서로 상대방의 락이 풀리길 기다리며 영원히 대기하는 상태
  • ex: 트랜잭션 A가 row1 잠금 + row2 대기, 트랜잭션 B가 row2 잠금 + row1 대기 → 서로가 서로를 기다려 무한 대기
  • DB가 주기적으로 Deadlock 감지 후 한쪽 트랜잭션을 강제 롤백시켜 해결

2-5. 비관적 락의 세분화

  • Shared Lock (공유 락, S-Lock)
    • 읽기 작업에 사용
    • 여러 트랜잭션이 동시에 S-Lock 보유 가능
    • S-Lock이 걸린 데이터에 X-Lock(쓰기)은 불가
  • Exclusive Lock (배타 락, X-Lock)
    • 쓰기 작업에 사용
    • 단 하나의 트랜잭션만 보유 가능
    • 다른 트랜잭션의 읽기/쓰기 모두 차단

DB vs MySQL vs InnoDB

계층 구조
DB (개념)
└── MySQL (DB 구현체 중 하나)
└── InnoDB (MySQL의 스토리지 엔진 중 하나)

각각 뭔지

DB (Database)

그냥 "데이터를 저장하는 시스템"이라는 개념
Oracle, MySQL, PostgreSQL, MongoDB 전부 DB

MySQL

DB를 구현한 소프트웨어 (RDBMS)
쿼리 파싱, 최적화, 캐시 등 담당
스토리지 엔진을 골라서 쓸 수 있음

InnoDB

MySQL 안에서 실제로 데이터를 읽고 쓰는 엔진
트랜잭션, 락, Undo Log, Redo Log 전부 여기서 처리

3. DB가 느린 이유

  • 디스크 I/O: WAL 로그 기록 + 실제 데이터 파일 수정 모두 디스크 접근 발생. RAM 대비 HDD는 수천 배, SSD도 수백 배 느림
  • Lock 경합: 동일 데이터에 다수 트랜잭션이 몰리면 Lock 대기 줄이 생기고, 대기 시간이 쌓이면 전체 처리량 급감
  • 버퍼 풀 관리: DB는 자주 쓰는 데이터를 메모리(버퍼 풀)에 캐싱. 버퍼 풀에 없는 데이터 접근 시 디스크 I/O 발생 (Page Fault)
  • 컨텍스트 스위칭: 멀티스레드 환경에서 스레드 간 전환에 CPU 비용 발생

4. Redis 구조와 속도

4-1. 인메모리

  • 모든 데이터를 RAM에 저장 → 디스크 접근 없음
  • RAM은 HDD 대비 수천 배, SSD 대비 수백 배 빠름
  • 단점: 메모리는 휘발성 → 서버 꺼지면 데이터 소멸
  • 보완 방법
    • RDB: 특정 시점의 스냅샷을 디스크에 저장
    • AOF: 모든 명령을 로그로 기록
    • 단, 두 방법 모두 활성화하면 디스크 I/O 발생 → 속도 저하 트레이드오프

4-2. 싱글 스레드 이벤트 루프

  • 명령을 하나씩 순서대로 처리
  • 동시에 두 개의 명령이 실행되지 않으므로 Lock 자체가 불필요
  • Lock 획득/해제 연산 없음, Deadlock 없음, Lock 경합 없음
  • Redis의 병목은 CPU가 아니라 네트워크 I/O → 싱글 스레드로도 충분한 처리량
  • Redis 6.0부터 네트워크 I/O 처리에 한해 멀티스레드 도입. 명령 실행 자체는 여전히 싱글 스레드

5. Redis 트랜잭션 — MULTI/EXEC

MULTI/EXEC -> Redis에서 트랜잭션을 구현하는 명령어

기본 사용법
MULTI ← 트랜잭션 시작

SET a 100
SET b 200
INCR a

EXEC ← 한꺼번에 실행DB

왜 쓰냐 ->
Redis는 싱글 스레드(일을 한 가지 주체가 순차적으로 실행)
→ MULTI/EXEC 사이 명령어들은 원자적으로 실행
→ 다른 클라이언트 명령이 중간에 끼어들지 못함

정리: Redis의 MULTI/EXEC는 완전한 트랜잭션이 아니라 명령어 묶음 실행 수준이고, ACID 중 완전한 보장은 못함.

즉, Redis는 싱글 스레드라 비관적 락(블로킹) 자체가 필요 없고, WATCH로 낙관적 락을 구현.

5-1. 동작 방식

  • MULTI 선언 후 명령들은 즉시 실행되지 않고 큐에 쌓임
  • EXEC 호출 시 큐의 명령들이 한 번에, 다른 클라이언트 개입 없이 순서대로 실행

5-2. DB 트랜잭션과의 차이

  • Atomicity (불완전)
    • EXEC 실행 중 특정 명령이 런타임 오류로 실패해도 나머지 명령은 계속 실행
    • 자동 롤백 없음
    • 단, 큐에 쌓는 단계(MULTI 이후)에서 문법 오류 발생 시 EXEC 자체가 실패하고 전체 취소
  • Isolation (완전 보장)
    • EXEC 실행 중 다른 클라이언트 명령 개입 불가
    • 싱글 스레드 구조 덕분에 자연스럽게 보장
  • Durability
    • AOF always 모드 설정 시 매 명령마다 디스크 기록 → 지속성 높아지나 속도 저하

6. Redis WATCH = 낙관적 락

6-1. 필요한 이유

  • MULTI/EXEC만으로는 Race Condition 방지 불가
  • EXEC 직전 다른 클라이언트가 내가 읽은 값을 변경하는 상황이 생길 수 있음

6-2. 동작 방식

WATCH balance       -- 이 키 감시 시작
GET balance         -- 현재값 읽기
-- 애플리케이션에서 로직 계산
MULTI
SET balance 500
EXEC                -- WATCH 이후 balance가 변경됐으면 nil 반환 (트랜잭션 취소)
  • WATCH 이후 해당 키가 변경되면 EXEC가 nil 반환 → 트랜잭션 전체 취소
  • 재시도 로직은 개발자가 직접 구현

6-3. <-> 비관적 락과의 차이

  • 비관적 락: 선점 후 차단 → 다른 트랜잭션이 아예 접근 못함
  • WATCH: 충돌 감지 후 취소 → 충돌이 드문 환경에서 성능 유리, 충돌이 잦으면 재시도 반복으로 비효율

7. 전체 비교표

항목관계형 DBRedis MULTI/EXECRedis WATCH
동시성 제어Lock (비관적)싱글 스레드 직렬화낙관적 락
Atomicity완전 보장, 자동 롤백런타임 오류 시 롤백 없음트랜잭션 취소만 (롤백 없음)
Consistency완전 보장제한적제한적
Isolation격리 수준 설정 가능완전 격리완전 격리
DurabilityWAL로 완전 보장설정에 따라 다름설정에 따라 다름
충돌 시 처리대기 or 롤백해당 명령만 실패, 나머지 진행트랜잭션 전체 취소
속도느림빠름빠름 (충돌 없을 때)
디스크 I/O항상 발생기본 없음 (설정 시 발생)기본 없음 (설정 시 발생)

8. 그래서

transanction acid 가 db 랑 어떻게 다른지-->

DB는 ACID를 완전하게 보장.
롤백도 되고, 격리 수준도 설정 가능하고, 디스크에 영구 저장.

이와 다르게

Redis는 ACID를 완전히 지원하지 않음.
MULTI/EXEC로 트랜잭션 비슷한 걸 제공하지만 런타임 오류 시 롤백이 없고, Durability도 설정에 따라 상이.

왜 빠른지 -->

DB가 느린 이유가 디스크 I/O + Lock 경합인데, Redis는 그 두 가지를 구조적으로 없앰.

인메모리 → 디스크 I/O 없음
싱글 스레드 → Lock 자체가 필요 없음

즉, ACID를 완전히 보장하는 비용을 포기한 대신 속도를 얻은 것이 Redis라고 할 수 있음.

profile
github: @dlwldP

0개의 댓글