[DB] 트랜잭션이란 ?

mallin·2022년 6월 1일
0

DB

목록 보기
4/4
post-thumbnail

모든 명령어의 성공 또는 실패 를 한꺼번에 모아서 처리 하는 것

명령어 중에 하나라도 실패하면 한 트랜잭션 내의 모든 명령어들이 무효화 (롤백) 된다.

은행에서 돈을 송금하려고 할 때

① 내 계좌에서 돈을 출금
② 다른 계좌로 돈을 입금

하는 과정이 함께 일어 나야 하는데 2번 과정에서 에러가 발생해서 입금이 되지 않았다면 돈이 붕 떠버리는 사태가 일어나게 됨

➡️ 이 경우엔 ①, ② 를 한 트랜잭션으로 묶어서 하나의 과정이라도 실패하면 롤백 되도록 해야함

트랜잭션의 성질 (ACID)

트랜잭션의 성질에는 총 4가지가 있다.

A tomicity (원자성)
트랜잭션은 부분적으로 완료 될 수 없으며, 전체가 다 되거나 아무것도 되지 않거나 둘 중 하나여야 한다
-> 출금하는 것만 성공하고 입금 되는 건 성공하면 안된다.

C onsistency (일관성)
트랜잭션이 실행을 성공적으로 완료하면 언제나 일관성 있는 데이터베이스 상태로 유지
-> 계좌 이체 하기 전 진행 전의 돈의 합과 진행 후의 돈의 합이 같아야 한다.

I solation (독립성,격리성)
트랜잭션을 수행 시 다른 트랜잭션의 연산 작업이 끼어들지 못하도록 보장
-> 출금 후 다른 트랜잭션이 끼어 들어 내 계좌에 남아 있는 금액을 보지 못해야 한다.

D urablility (영속성,지속성)
성공적으로 수행된 트랜잭션은 영원히 반영
-> 완료 후 시스템 문제가 생기더라도 커밋이 되었다면 DB 에 영구적으로 반영되어야 한다.

하지만 ACID 원칙을 완벽하게 지키게 되면 동시성이 매우 떨어지기 때문에 완벽하게 지켜지지 않는 경우도 있다.

그렇기 때문에 ACID 원칙을 희생하여 동시성을 얻을 수 있는 방법인 isolation Level (격리 수준) 을 제공한다.

트랜잭션 격리 수준 (isolation Level)

격리 수준은 3가지 문제가 발생하는지에 따라 분류 한다.
① Dirty Read ② Non-Repeatable Read ③ Phantom Read

① Dirty Read (비커밋 읽기)
트랜잭션에서 커밋되기 전 작업 내용을 다른 트랜잭션에서 볼 수 있다

EX) A 라는 트랜잭션에서 id : 22, name: 이름을 insert 하고 커밋하지 않은 상태에서 B 라는 트랜잭션이 id: 22 를 select 하는 경우 읽을 수 있음. 이때 A 트랜잭션에서 커밋되지 않고 롤백되는 경우 데이터 정합성에 문제가 생긴다.

👉 가능한 이유
InnoDB 엔진이 transaction 을 커밋하는 방식 때문에 가능하다.
InnonDB 는 일단 commit 이 되지 않더라도 실행한 모든 쿼리를 DB 에 적용한다.
즉, 특별히 log를 보고 특정 시점의 snapshot을 복구하는 consistent read를 하지 않고 그냥 해당 시점의 DB 를 읽으면 dirty read 로 간주한다.

consistent read 란 ?
select 시 현재 DB 값이 아닌 특정 시점의 DB snapshot 을 읽어 오는 것

이 snapshot 은 commit 된 변화만이 저장된 것을 의미한다.

InnoDB 엔진은 각 쿼리를 실행할 때마다 실행한 쿼리의 log를 차곡차곡 저장한다.
그리고 나중에 consistent read를 할 때 이 log를 통해 특정 시점의 DB snapshot을 복구하여 가져온다.

② Non-Repeatable Read (비반복 읽기)
한 트랜잭션에서 같은 쿼리를 두번 실행시 그 사이 다른 트랜잭션이 값을 수정, 삭제 해서 두 쿼리의 결과가 상이하게 나타나는 현상을 의미한다.

③ Phantom Read (가상 읽기)
한 트랜잭션 안에서 일정 범위의 레코드를 두번 이상 읽을 때, 첫번째 쿼리에서 없던 레코드가 두번째 쿼리에서 나타나는 현상을 의미한다.

위의 3가지 문제가 발생하는지 여부에 따라서 트랜잭션 격리 수준이 결정되는데, 그건 아래 표와 같이 결정된다. ⬇️

분리 레벨Dirty ReadNon-Repeatable ReadPhantom Read
READ UNCOMMITEEDOOO
READ COMMITTED-OO
REPEATABLE READ--O
SERIALIZABLE---

① READ UNCOMMITTED

  • Dirty Read / Non-Repeatble Read / Phantom Read 가 발생한다.
  • SELECT 쿼리를 실행 할 때 다른 트랜잭션에서 커밋되지 않은 데이터를 읽을 수 있다.
  • 오라클은 해당 레벨을 지원하지 않는다.

② READ COMMITTED

  • Non-Repeatable Read / Phantom Read 가 발생한다.
  • 커밋 완료된 데이터만 읽을 수 있는 격리수준
  • DB2, SQL Server, Sybase의 경우 S Lock을 이용해서 구현한다.
    하나의 레코드를 읽을 때 Lock을 설정하고 해당 레코드에서 빠지는 순간 Lock을 해제한다.
    (Select 문으로 읽을 때만 해당 Row 에 대해 S Lock 이 걸린다)
  • Oracle은 Lock을 사용하지 않고 쿼리시작 시점의 Undo 데이터를 제공하는 방식으로 구현한다.
  • Record lock 만 사용하고 Gap lock 사용 하지 않는다.

③ REPEATABLE READ

  • Phantom Read 가 발생한다.
  • 하나의 트랜잭션에서 반복되는 SELECT 는 항상 같은 값을 조회한다.
  • 처음 read 한 시간을 기록하고 그 이후 모든 select 마다 처음 시점을 기준으로 consistent read 를 수행한다. (Non-Repeatable Read 발생 X)
  • 트랜잭션이 완료 될 때까지 해당 row 에 Shared Lock 이 걸리기 때문에 해당 데이터에 대한 갱신, 삭제가 불가능하다.
  • InnoDB 엔진에서 사용하는 기본 격리 수준이다.
  • DB2, SQL Server의 경우 트랜잭션 고립화 수준을 Repeatable Read 로 변경하면 읽은 데이터에 걸린 S Lock을 커밋할 때까지 유지하는 방식으로 구현한다.
  • Oracle은 이 레벨을 명시적으로 지원하지 않지만 for update절을 이용해 구현 가능하다.

④ SERIALIZABLE

  • Dirty Read, Non-Repeatble Read, Phantom Read 모두 발생하지 않는다.
  • 트랜잭션이 완료 될 때까지 그 영역에 해당하는 데이터 UPDATE 및 INSERT 가 불가능하다.
  • 기본적으로 REPEATABLE READ 와 동일하지만 SELECT 쿼리가 전부 SELECT ... FOR SHARE로 자동 변경된다. 그로인해 모든 select row 에 대해 S Lock 이 걸리고 X Lock (update, delete 등 불가능) S Lock 걸린 row 에 update, delete 시도 시 deadlock이 발생한다.
  • 동시성이 가장 떨어지고 쉽게 Dead Lock 에 걸릴 수 있다.
  • 많이 사용하지 않는다.

Rails & Spring 에서 트랜잭션 사용하기

위에서 이론에 대해서 알았으니, 이제 실질적으로 Rails 와 Spring 에서 트랜잭션을 어떻게 적용하면 되는지 알아보자

💎 Rails 에서 트랜잭션 사용하는 법

ActiveRecord::Base.transaction do ## 예외 발생 시 롤백 
rescue => e ## 예외 발생한 경우 처리 
end

위 코드처럼 트랜잭션을 사용하면 되고, 만약 트랜잭션 격리 수준을 직접 설정해주고 싶은 경우에는 아래 처럼 isolation 을 직접 지정해주면 된다.

transaction(isolation: :?)
:read_uncommited / :read_commited / :repeatable_read / :serializable

☕️ 스프링에서 트랜잭션 사용하는 법

@Transactional
스프링에선 해당 어노테이션을 메소드, 클래스 위에 사용하면 트랜잭션을 사용할 수 있다.
스프링의 경우에도 @Transactional(isolation=Isolation.SERIALIZABLE) 로 격리 수준을 직접 설정해줄 수 있다.

0개의 댓글