DB

트랜잭션 정리(개념/ 특성/ 상태/ 수행방법/ 격리수준/ 병행 제어)

吳버플로우 2025. 3. 6. 20:17

트랜잭션(Transaction)이란?

하나의 작업을 수행하는 데 필요한 데이터베이스의 연산들을 모아놓은 것을 말합니다.
데이터베이스에서 논리적인 작업의 단위를 일컫습니다.

트랜잭션의 특성 (ACID)

트랜잭션은 ACID 원칙을 따르며 데이터의 일관성을 유지합니다.

 

1️⃣ Atomicity (원자성)

  • 트랜잭션 내의 연산이 모두 실행되거나, 하나라도 실패하면 전부 취소되어야 합니다. (DB에 전부 반영되거나, 전혀 반영되지 않거나 all or nothing)
  • 예: 계좌 이체 시, 송금하는 계좌에서 돈이 빠져나가고 수취 계좌에 입금이 되어야 함

2️⃣ Consistency (일관성)

  • 트랜잭션이 수행된 후에도 데이터베이스가 일관성 있는 상태여야 한다는 것을 의미합니다. 트랜잭션이 수행되더라도 데이터베이스의 규칙(무결성 제약 조건, 관계 규칙 등)이 항상 지켜져야 합니다.
  • 예: 한 계좌에서 돈을 빼고 다른 계좌에 넣을 때, 총 잔액이 변하지 않도록 유지

3️⃣ Isolation (고립성/ 격리성)

  • 여러 트랜잭션이 동시에 실행될 때, 각 트랜잭션이 다른 트랜잭션의 작업에 영향을 받지 않도록 보장해야 합니다. 수행 중인 트랜잭션이 완료될 때까지, 트랜잭션이 생성한 중간 연산 결과에 다른 트랜잭션들이 접근할 수 없음을 의미합니다.
  • 이를 보장하기 위해 병행 제어가 사용됩니다.
  • 예: A와 B가 동시에 같은 계좌에서 출금할 때, 각각의 트랜잭션이 안전하게 처리되어야 함.

4️⃣ Durability (지속성/ 영속성)

  • 트랜잭션이 커밋된 후에는 어떠한 경우(시스템 장애)에도 데이터가 영구적으로 저장되어야 한다.
  • 예: 전산 오류나 서버 다운이 발생해도 이전에 완료된 이체 기록은 유지됨.

 

이와 같은 특성으로 인해, 트랜잭션은 데이터 정합성을 보장하고, 오류나 장애 발생 시 안전하게 복구하기 위해 필요합니다. 또한, RDBMS는 트랜잭션을 지원하며 SQL 표준에서 정의된 ACID 속성을 따릅니다.


 

트랜잭션 상태 

트랜잭션은 5가지 상태(실행, 부분 완료, 완료, 실패, 철회)를 갖습니다.

출처: 모두의 SQL 책

상태 설명
 실행 트랜잭션이 시작되고 정상적으로 실행 중인 상태, 연산 가능
 부분 완료 트랜잭션이 모든 연산을 수행 완료했지만, 아직 커밋이 되지 않은 상태, 오류시 rollback 가능
 완료 트랜잭션이 성공적으로 완료되어 COMMIT이 수행 된 상태,
데이터베이스에 영구적으로 반영됨 (Rollback 불가)
 실패 트랜잭션 실행 중 오류(제약 조건 위반, 데이터 충돌, 시스템 장애 등)가 발생하여 정상적으로 완료되지 못한 상태
 철회 트랜잭션이 실패하여 ROLLBACK이 수행된 상태,
트랜잭션이 복원되어 트랜잭션 수행 이전 상태로 돌아감

트랜잭션의 수행 방법

트랜잭션의 수행 방법은 크게 직렬 수행과 병행 수행 두가지 방식으로 나눠 볼 수 있습니다.

 

1️⃣ 직렬 수행 (Serial Execution)

직렬 수행은 트랜잭션들이 순차적으로 실행되는 방식입니다. 즉, 한 트랜잭션이 끝난 후에 다른 트랜잭션이 실행됩니다.

 

장점:

  • 데이터의 일관성을 쉽게 보장할 수 있습니다.
  • 동시에 처리되는 트랜잭션이 없기 때문에, 충돌이 발생하지 않습니다.

단점:

  • 효율성이 낮습니다. 여러 트랜잭션이 순차적으로 처리되므로, 전체 성능이 떨어질 수 있습니다.

 

2️⃣ 병행 수행 (Concurrent Execution):

  • 병행 수행은 여러 트랜잭션이 동시에 실행되거나, 서로 겹쳐서 실행되는 방식입니다. 트랜잭션들이 동시에 처리되므로 시스템 성능을 높이고 자원을 더 효율적으로 사용할 수 있습니다.
  • 병행 수행에서는 인터리빙(Interleaving) 방식으로 트랜잭션들이 교차하며 실행됩니다. 즉, 트랜잭션 A의 연산을 한 부분 실행한 후, 트랜잭션 B의 연산을 실행하고 다시 트랜잭션 A의 나머지 연산을 실행하는 방식입니다.

장점:

  • 성능이 향상됩니다. 여러 트랜잭션을 동시에 처리할 수 있으므로 시스템의 처리 속도가 빨라집니다.
  • 자원 효율성이 높아집니다. 여러 작업이 동시에 이루어져서 CPU와 메모리 자원을 잘 활용할 수 있습니다.

단점:

  • 동시성 문제가 발생할 수 있습니다. 예를 들어, *더티리드, 비일관성, 교착 상태 등이 발생할 수 있습니다.
  • 이를 해결하기 위해 병행 제어 기법이 필요합니다.

* 더티리드: 한 트랜잭션이 아직 커밋되지 않은 다른 트랜잭션의 데이터를 읽는 현상을 말합니다.

 

인터리빙 방식이란?

인터리빙 방식에서는 각 트랜잭션의 연산이 완전히 끝날 때까지 기다리지 않고, 트랜잭션들의 연산을 일정 부분씩 번갈아 가며 실행합니다. 즉 , 각 트랜잭션이 다른 트랜잭션의 커밋을 기다리지 않습니다.

이 방식은 병행 처리(Concurrency Control)에서 중요한 개념으로, 트랜잭션들의 성능을 높이고 자원을 효율적으로 사용할 수 있도록 합니다.

 

read(x): 메인메모리 버퍼 블록에 저장되어 있는 데이터 x를 프로그램의 변수로 읽어오는 연산
write(x): 프로그램의 변수 값을 메인 메모리 버퍼 블록에 있는 데이터 x에 기록하는 연산

 

*메인 메모리 버퍼 블록: 메인 메모리에 임시로 저장된 블록

 

예를 들어,

  • 트랜잭션 T1: R(X) → W(X)
  • 트랜잭션 T2: R(Y) → W(Y)

인터리빙 방식에서는 위와 같은 트랜잭션들이 번갈아가며 실행될 수 있습니다.

 
T1: R(X) → T2: R(Y) → T1: W(X) → T2: W(Y)

또는

T1: R(X) → T1: W(X) → T2: R(Y) → T2: W(Y)
 

이렇게 서로 다른 트랜잭션이 교차하면서 실행될 수 있습니다.


트랜잭션 스케줄

트랜잭션 스케줄은 동시에 실행되는 트랜잭션들의 연산 순서를 나타냅니다. 

데이터베이스의 일관적인 상태를 유지하기 위해서 순서를 정의하는 것이 중요합니다.

트랜잭션 스케줄 의미
직렬 스케줄 인터리빙 방식을 이용하지 않고 트랜잭션별로 연산들을 순차적으로 실행시키는 것
독립적으로 수행하기 때문에 병행 수행이라 할 수 없고 일반적으로 잘 사용하지 않음
비직렬 스케줄 인터리빙 방식을 이용하여 트랜잭션들을 병행해서 수행시키는 것
정확성을 보장할 수 없다.
직렬 가능 스케줄 직렬 스케줄과 같이 정확한 결과를 생성하는 비직렬 스케줄
모든 비직렬 스케줄이 직렬 가능 스케줄이 아니기에 확인작업이 필요하다. 하지만 매번 판단하는 것이 쉽지 않기에 대부분 DBMS에서는 직렬 가능성을 보장하는 병행 제어 기법을 사용한다.

 


병행 제어 기법 (동시성 제어)

병행 수행에서 발생하는 문제를 해결하기 위해 트랜잭션의 실행을 제어하는 방법을 말합니다.

이는 트랜잭션이 병행 수행될 때 트랜잭션이 데이터베이스의 일관성을 파괴하지 않고, 다른 트랜잭션에 영향을 주지 않도록 합니다.

1️⃣ 로킹 (Locking)

로킹은 트랜잭션이 접근하려는 데이터를 다른 트랜잭션이 접근하지 못하도록 잠그는(lock) 병행 제어 기법입니다. 병행 제어 기법 중 가장 많이 사용됩니다.

 

로킹 단위: 한 번에 로킹 할 수 있는 데이터의 크기

필드(Field), 레코드(Record), 테이블(Table), 파일(File), 데이터베이스(Database) 모두 로킹 단위가 될 수 있습니다.

 

로킹 단위의 크기에 따라 성능의 차이가 발생합니다.

로킹 단위 병행성 오버헤드
낮아짐 감소
작음 높아짐 증가

 

로킹 연산

  • lock: 데이터에 대한 독점권 보장
  • unlock: 데이터에 대한 독점권 포기
     

하지만 로킹은 직렬가능성을 완전히 보장할 수 없습니다.

로킹의 독점성으로 인하여, 하나의 트랜잭션만이 공유 데이터를 사용할 수 있는데, 실제로 오직 읽기(read)만 하는 경우에는 동시에 접근해도 문제가 없기 때문에 이런 경우에 효율적이지 못합니다. 
따라서 이러한 이유로 2단계 로킹 규약이 사용됩니다.

또한 로킹규약은 데드락이 발생할 수 있습니다.

 

2단계 로킹 규약

트랜잭션 스케줄의 모든 트랜잭션이 2단계 로킹 규약을 준수하면, 해당 스케줄은 직렬 가능성이 보장됩니다. 즉 직렬 가능성을 보장하기 위해 2단계 로킹 규약이 사용됩니다.

  • Lock과 Unlock이 동시에 이루어지면 일관성이 보장되지 않으므로 Lock만 가능한 단계와 Unlock만 가능한 단계를 구분
  • 확장단계 : 새로운 Lock은 가능하고 Unlock은 불가능하다.
  • 축소단계 : Unlock은 가능하고 새로운 Lock은 불가능하다.

2️⃣ 타임스탬프(Time Stamp)

데이터에 접근하는 시간을 미리 정해서 정해진 시간의 순서대로 데이터에 접근하여 수행하는 기법을 말합니다.


병행 제어의 문제점 

병행 제어 시 크게 세가지 문제점(갱신분실, 모순성, 연쇄복귀)이 발생할 수 있습니다.

1️⃣ 갱신 손실 

갱신 분실은 하나의 트랜잭션이 수행한 변경된 연산의 결과를 다른 트랜잭션이 덮어써 전의 변경된 연산이 무효화되는 것입니다.

출처: 데이터베이스 개론 책

T1 이 X를 4000으로 변경했지만 T2가 나중에 X를 1500으로 덮어써서 T1의 갱신 내용(4000으로 변경)이 완전히 사라지게 됩니다. 즉, T₁의 작업 결과가 T₂에 의해 덮어써져 손실되며 갱신 분실이 발생하였습니다.

 

2️⃣ 모순성

모순성은 하나의 트랜잭션이 여러개의 데이터 변경 연산을 실행할 때 일관성 없는 상태의 데이터베이스에서 데이터를 가져와 연산을 실행함으로써 모순된 결과가 발생하는 것입니다.

T1은 X를 수정하고 Y를 수정하는 사이, T2가 T₁이 수정한 X를 읽고 다시 수정한 후, Y도 읽고 수정하였습니다. 그래서 T1이 Y를 읽을 때는 이미 T2가 Y를 수정한 상태로, X는 초기값인 상태를 읽었고, Y는 변경된 값을 읽었으므로 일관되지 않은 상태의 데이터를 읽게 되었습니다. 따라서 모순성이 발생하였습니다.

 

3️⃣ 연쇄복귀

연쇄복귀는 트랜잭션이 완료되기 전에 장애가 발생하여 rollback 연산을 수행하면, 이 트랜잭션이 장애 발생 전에 변경한 데이터를 가져가 변경 연산을 실행한 또 다른 트랜잭션에도 rollback 연산을 연쇄적으로 실행해야 한다는 것입니다.

한 트랜잭션(T1)이 수정한 데이터를 다른 트랜잭션(T2)이 읽고 처리한 상황에서 트랜잭션(T₁)이 실패하였습니다. 하지만 이미지의 하단 설명에 따르면, T₂는 이미 완료(commit)된 트랜잭션이므로 롤백할 수 없는 상황입니다.

 

따라서 병행 수행에서 트랜잭션들이 어떻게 순서대로 실행될지에 대한 규칙을 정의하는 것이 필요합니다.


트랜잭션 격리 수준

병행 제어를 적용할 때 트랜잭션 간의 격리 수준을 조정하여 문제를 방지할 수 있습니다.

트랜잭션 격리 수준에는 4가지 주요 수준이 있습니다.

격리 수준 더티 리드 비반복 읽기 팬텀 읽기 설명
READ UNCOMMITTED 가능 가능 가능 가장 낮은 격리 수준으로 다른 트랜잭션이 커밋되지 않은 데이터를 읽을 수 있습니다.
성능은 우수하나, 데이터 일관성을 보장할 수 없습니다.
READ COMMITTED 불가능 가능 가능 트랜잭션이 커밋된 데이터만 읽을 수 있습니다.
Oracle 의 기본 격리 수준
REPEATABLE READ 가능 가능 가능 트랜잭션이 읽은 데이터를 다른 트랜잭션이 수정할 수 없도록 보장합니다.
MySQL의 기본 격리 수준
SERIALIZABLE 가능 가능 가능 가장 높은 격리 수준으로, 직렬가능한 결과를 보장합니다.
성능은 가장 낮지만 데이터 일관성은 가장 높습니다.
    • 비반복 읽기(Non-repeatable Read): 한 트랜잭션 내에서 같은 데이터를 두 번 읽었을 때 결과가 다른 경우( ∵ 다른 트랜잭션이 데이터를 변경하고 커밋함)
    • 팬텀 읽기(Phantom Read): 한 트랜잭션 내에서 같은 쿼리를 두 번 실행했을 때 처음에는 없던 데이터가 나중에 나타나는 경우(∵다른 트랜잭션이 새 데이터를 삽입하고 커밋함)

자바를 사용하는 백엔드 개발자일 경우, @Transactional 어노테이션을 통해 트랜잭션을 쉽게 시작할 수 있습니다.

@Transactional

어노테이션이 붙은 메서드가 실행되면 트랜잭션이 자동으로 시작되며, 메서드는 트랜잭션 단위로 실행되게 됩니다. 

정상 실행시 Commit이 되고, 오류가 발생하게 된다면 save 등 메서드 내부의 명령어가 Rollback이 됩니다.

 

또한 필요 시 메서드별 트랜잭션 격리수준을 설정할 수 있습니다. 어노테이션에서 격리 수준을 설정하지 않으면, 데이터베이스별 자동으로 기본 격리 수준이 적용됩니다.

@Transactional(isolation = Isolation.SERIALIZABLE)

application.yml 파일에서 트랜잭션 격리 수준을 지정할 수 있지만, 메서드별로 격리 수준을 설정하는 방식이 더 일반적입니다. 


😭여담

원래 블로그에 여담을 잘 남기지는 않지만..

4시간 가량 글을 쓰다가 너무 오래 글을 쓴 것 때문인지.. 사이트가 한번 꺼져서 쓴 글이 한 번에 싹 날라갔습니다...

임시저장이 자동으로 되지않았고.. 임시저장을 생활화해야겠습니다.🔥

화이팅