[DB] 트랜잭션

·2025년 1월 26일

데이터베이스

목록 보기
21/22
post-thumbnail

트랜잭션 (Transaction)


Connection & Session

Connection

  • 클라이언트와 데이터베이스 서버 간의 물리적인 네트워크 연결

Session

  • Conection이 생성된 후 특정 클라이언트와 데이터베이스 서버 간의 논리적인 작업 영역
  • Session은 Connection 위에서 동작하며, 하나의 Connection에 대해 하나의 Session이 생성됨
  • Session은 SQL 쿼리 실행, 트랜잭션 관리 등을 함

트랜잭션이란?

  • 트랜잭션(Transaction)은 데이터베이스에서 하나의 논리적 작업 단위를 의미
  • 여러 작업을 하나의 그룹으로 묶어 실행하며, 이 모든 작업이 성공(Commit)하거나 실패(Rollback)해야 데이터의 무결성과 일관성을 유지할 수 있음
  • 트랜잭션은 데이터베이스 시스템에서 ACID 성질을 보장
  • 일반적으로 데이터베이스를 변경하는 INSERT 문, DELETE 문, UPDATE 문의 실행을 트랜잭션으로 관리함
  • 세션마다 트랜잭션이 관리됨

트랜잭션의 주요 요소

Commit

  • 트랜잭션을 종료하며 트랜잭션에서 수행된 모든 작업을 데이터베이스에 영구적으로 저장하는 작업
  • 트랜잭션이 성공적으로 수행되었음을 선언

예시

START TRANSACTION; -- 트랜잭션 시작
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
COMMIT; -- 변경 사항 확정

Rollback

  • 트랙잭션을 종료하며 트랜잭션에서 수행된 모든 작업을 취소하고 원래 상태로 복구하는 작업
  • 트랜잭션이 수행을 실패했음을 선언

예시

START TRANSACTION; -- 트랜잭션 시작
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
-- 오류 발생
ROLLBACK; -- 변경 사항 취소

Auto Commit

  • Auto Commit이 활성화된 상태에서는 별도로 COMMIT 명령을 실행하지 않아도 SQL 문이 실행될 때마다 자동으로 Commit이 수행
  • MySQL, PostgreSQL 등 많은 데이터베이스는 기본값으로 Auto Commit 모드가 활성화되어 있음

예시

SET autocommit = 0; -- Auto Commit 비활성화
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT; -- 명시적으로 Commit 필요

SET autocommit = 1; -- Auto Commit 활성화
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
-- 이 명령은 자동으로 Commit

트랜잭션 특징 - ACID

원자성(Atomicity)

  • 트랜잭션에 포함된 모든 작업이 모두 성공하거나 모두 실패해야 한다는 것을 보장
  • 작업 도중 실패가 발생하면, 트랜잭션이 수행한 모든 변경 사항이 롤백

예시:
계좌 이체에서 돈을 보내는 작업과 받는 작업 중 하나라도 실패하면 전체 작업이 취소됨

일관성(Consistency)

  • 트랜잭션이 완료된 후 데이터베이스는 항상 일관된 상태를 유지해야 함
  • 데이터베이스의 제약 조건(무결성, 고유성 등)이 트랜잭션 전후에도 유지됨

예시:
은행 계좌의 총 잔액이 트랜잭션 전후로 동일해야 함

격리성(Isolation)

  • 동시에 실행되는 트랜잭션이 서로의 작업에 영향을 미치지 않도록 보장
  • 여러 트랜잭션들이 동시에 수행되어도 그 결과는 순차적으로 실행된 결과와 같아야함

예시:
A계좌에서 B계좌로 5,000원을 이체하는 트랜잭션을 수행하는 동안에는 다른 트랜잭션이 A계좌와 B계좌에 접근할 수 없어야 함

지속성(Durability)

  • 트랜잭션이 성공적으로 완료되면 해당 작업 결과가 영구적으로 반영되어야 함
  • 시스템 장애가 발생하더라도 트랜잭션 완료 내용은 데이터베이스에 보존됨

예시:
A계좌에서 B계좌로 5,000원을 이체하는 트랜잭션이 성공적으로 완료된 후에는 항상 이체가 완료된 상태를 유지해야함
은행 이체가 완료된 후 전원이 나가도 송금 내역은 남아 있어야 함


트랜잭션 격리 수준 (Transaction Isolation level)

  • 트랜잭션 격리 수준은 동시에 실행되는 트랜잭션 간의 데이터 격리 정도
  • 동시에 여러 트랜잭션이 처리될 때 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있도록 허용할지 말지를 결정하는 것
  • 격리 수준이 높을수록 데이터 일관성은 좋아지지만, 성능은 낮아질 수 있음

1. READ UNCOMMITTED

  • 다른 트랜잭션이 커밋하지 않은 변경 내용도 읽을 수 있음
  • 가장 낮은 격리 수준으로, 성능은 높지만 데이터 무결성 문제가 생길 가능성이 큼
  • 쉽게 말해 완료되지 않은 트랜잭션의 작업 결과를 읽을 수 있는 레벨
    한다.
  • Dirty Read, Non-Repeatable Read, Phantom Read 현상이 발생한다.

2. READ COMMITTED

  • 다른 트랜잭션이 커밋한 변경 내용만 읽을 수 있음
  • 즉, 트랜잭션이 완료(Commit)되지 않은 상황에서 다른 트랜잭션이 변경한 데이터 SELECT가 가능하지 않는 레벨
  • 트랜잭션이 동일한 데이터를 두 번 조회할 때, 그 사이에 다른 트랜잭션이 Commit하면 값이 바뀔 수 있음
  • Non-repeatable read, Phantom read 현상이 발생

3. REPEATABLE READ

  • 트랜잭션 내에서 동일한 데이터를 여러 번 읽더라도 항상 동일한 결과를 반환
  • 하지만 다른 트랜잭션이 새로운 데이터를 삽입했을 때 현재 트랜잭션에서 새로운 데이터에 대한 조회가 가능함
  • Phantom Read 현상이 발생
  • MySQL InnoDB의 기본 트랜잭션 격리 수준(이때 Phantom Read는 발생하지 않음)

MySQL InnoDB에서는 MVCC(Multi-Version Concurrency Control)를 통해 읽기 일관성을 유지하고, Next-Key Lock(레코드와 인덱스 간 갭에 대한 잠금)을 통해 삽입으로 인한 데이터 변화까지 방지하기 때문에 Repetatable Read 격리수준이어도 팬텀리드 현상은 일어나지 않음

※ MVCC (Multi-Version Concurrency Control)

  • MVCC는 데이터베이스에서 다중 버전 동시성 제어를 제공하는 기술로, 트랜잭션이 시작될 때 생성된 스냅샷(Snapshot)을 기반으로 데이터를 읽음으로써 트랜잭션 간의 읽기-쓰기 충돌을 방지함
  • 데이터 변경 시 기존 데이터를 덮어쓰지 않고 새로운 버전을 생성하며, 읽기 작업은 스냅샷을 참조하여 일관된 데이터를 제공함
  • 이를 통해 동시성을 높이고 읽기 작업 중 락 경합을 줄일 수 있음

※ Next-Key Lock

  • Next-Key Lock은 MySQL InnoDB에서 레코드 잠금(Record Lock)갭 잠금(Gap Lock)을 결합한 잠금 메커니즘으로, 특정 레코드뿐만 아니라 인덱스 범위(갭)도 잠금으로 보호함
  • 이를 통해 다른 트랜잭션이 동일한 인덱스 범위 내에서 데이터를 삽입하거나 수정하지 못하게 하며, 팬텀 리드(Phantom Read) 현상을 방지하고 데이터 일관성을 유지함

4. SERIALIZABLE

  • 트랜잭션이 접근 중인 데이터에 대해 다른 트랜잭션의 모든 접근이 불가능함
  • 트랜잭션이 접근 중인 데이터에 대해 다른 트랜잭션이 변경, 삭제하지 못할 뿐만 아니라 새로운 데이터 삽입도 불가능함
  • 가장 높은 격리 수준으로, 트랜잭션이 순차적으로 실행되도록 보장

트랜잭션에서 발생 할 수 있는 문제

  • 트랜잭션이 동시에 실행될 때, 격리 수준(Isolation Level)이 낮으면 여러 가지 데이터 일관성 문제가 발생할 수 있음
  • 대표적인 문제들은 Dirty Read, Non-Repeatable Read, Phantom Read가 있음

Dirty Read

  • Commit되지 않은 데이터를 다른 트랜잭션이 읽는 문제
  • 트랜잭션 A가 아직 Commit 또는 Rollback하지 않은 데이터를 트랜잭션 B가 읽어버릴 수 있음
  • 만약 트랜잭션 A가 Rollback하면, 트랜잭션 B는 존재하지 않는 데이터를 읽은 셈이 됨 (일관성 깨짐)

예시

-- 트랜잭션 A: 사용자 1의 잔액을 5000으로 변경했지만 아직 Commit하지 않음
BEGIN;
UPDATE users SET balance = 5000 WHERE id = 1;
-- (트랜잭션 A는 아직 Commit 또는 Rollback 안 함)

-- 트랜잭션 B: 변경된 값을 읽음
SELECT balance FROM users WHERE id = 1; -- 결과: 5000 (그러나 아직 확정된 값이 아님)

-- 트랜잭션 A: Rollback 수행
ROLLBACK;

-- 트랜잭션 B: 다시 읽을 경우 기존 값이어야 하지만 Dirty Read가 발생했음
SELECT balance FROM users WHERE id = 1; -- 결과: 3000 (Dirty Read 발생!)

Non-Repeatable Read

  • 같은 데이터를 두 번 조회했을 때 값이 다를 수 있는 문제
  • 첫 번째 조회 이후 다른 트랜잭션이 해당 데이터를 변경 후 Commit하면, 다시 조회했을 때 값이 달라질 수 있음

예시

-- 트랜잭션 A: 사용자 1의 잔액을 조회
BEGIN;
SELECT balance FROM users WHERE id = 1; -- 결과: 3000

-- 트랜잭션 B: 같은 데이터를 변경 후 Commit
BEGIN;
UPDATE users SET balance = 5000 WHERE id = 1;
COMMIT;

-- 트랜잭션 A: 다시 조회하면 값이 변경되어 있음 (Non-Repeatable Read 발생)
SELECT balance FROM users WHERE id = 1; -- 결과: 5000 (이전과 다름!)

Phantom Read

  • 같은 조건으로 조회했을 때, 추가된 데이터로 인해 결과가 달라지는 문제
  • 다른 트랜잭션이 새로운 데이터를 INSERT하거나 DELETE하면, 동일한 SELECT 결과가 달라질 수 있음

예시

-- 트랜잭션 A: 현재 시스템에 등록된 사용자를 조회
BEGIN;
SELECT * FROM users WHERE balance > 5000;
-- 결과: 2명의 사용자

-- 트랜잭션 B: 새로운 사용자 추가
BEGIN;
INSERT INTO users (id, name, balance) VALUES (3, '새로운 사용자', 6000);
COMMIT;

-- 트랜잭션 A: 동일한 조건으로 다시 조회하지만 새로운 데이터가 추가됨 (Phantom Read 발생)
SELECT * FROM users WHERE balance > 5000;
-- 결과: 3명의 사용자 (이전보다 1명 증가)
격리 수준Dirty Read (더티 리드)Non-Repeatable Read (비반복 가능 읽기)Phantom Read (팬텀 리드)
READ UNCOMMITTED✅ 발생 가능✅ 발생 가능✅ 발생 가능
READ COMMITTED❌ 방지됨✅ 발생 가능✅ 발생 가능
REPEATABLE READ❌ 방지됨❌ 방지됨✅ 발생 가능
SERIALIZABLE❌ 방지됨❌ 방지됨❌ 방지됨

0개의 댓글