트랜잭션 (Transaction)

JoonYoung Maeng·2022년 3월 22일
0
post-thumbnail

⁉️트랜잭션이란 ?

트랜잭션 (Transaction) 반드시 한번에 이뤄지거나 이뤄지지 않아야하는 작업의 단위를 말한다.

따라서, 트랜잭션이 발생 후 성공하면 데이터베이스에 완벽히 접근 혹은 데이터의 수정이 일어나야 하며 실패한다면 트랜잭션이 발생하기 이전 상태로 돌아가 데이터베이스 내 데이터들의 완전성을 보장해준다.

🏧 트랜잭션 연산 및 상태

트랜잭션 연산

트랜잭션의 연산에는 2가지가 있다.

  1. Commit (커밋) : 하나의 트랜잭션이 작업을 성공적으로 수행하여 데이테베이스가 다시 일관된 상태를 가질 때 트랜잭션이 완료되었다는 메세지를 알려주는 연산이다.
  2. Rollback (롤백) : 하나의 트랜잭션이 작업 수행도중 실패하여 데이터베이스의 일관성이 깨지게 된 경우 작업이 수행되기 이전의 데이터베이스의 일관성 상태로 돌아가기 위해 트랜잭션에서 수행했던 작업들을 취소시키는 연산이다.

트랜잭션 상태

  1. Active (활동) : 트랜잭션이 시작했고 동작중인 상태를 의미한다.
  2. Partially Committed (부분 완료) : 트랜잭션에서 실행한 모든 쿼리문이 완료되었지만, 최종적으로 데이터베이스에 반영하지 않은 상태 (Commit 명령을 수행하지 않은 상태)를 의미한다.
  3. Committed (완료) : 트랜잭션 작업이 정상적으로 마무리되어 종료된 상태를 의미한다.
  4. Failed (실패) : 트랜잭션이 작업 수행 도중 비정상적으로 종료되어 실패한 상태를 의미한다.
  5. Aborted (철회) : 트랜잭션이 실패 후 트랜잭션이 취소되고 이전 상태로 돌아간 상태를 의미한다.

image

⚛️ 트랜잭션의 특징 (ACID)

트랜잭션이 데이터베이스 내 데이터들의 완전성을 보장하기 위해서 가져야하는 4가지 특징들이 있다. 이 4가지 특징들의 알파벳 앞자리를 가져와 흔히 “ACID” 라고 말한다.

✔️ 원자성 (Atomicity)

트랜잭션이 수행하던 도중 비정상적으로 종료되게 되면 종료되기 이전 정상적으로 수행되었던 작업이 데이터베이스에 반영된다면 데이터베이스를 신뢰할 수 없을 것이다.

따라서, 트랜잭션을 통해 변경되는 데이터베이스의 신뢰성을 보장하기 위해 모든 트랜잭션 작업들이 정상적으로 수행된 후 반영되던지 모든 작업이 수행되지 않고 이전 데이터베이스 상태를 유지해야 한다.

✔️ 일관성 (Consistency)

트랜잭션 작업이 성공적으로 완료된 이후에도 해당 트랜잭션 작업이 일어나기 전과 동일하게 데이터베이스 내 데이터들을 일관성있게 보장해야한다.

✔️ 격리성 (Isolation)

일련의 다른 트랜잭션 작업들이 동시에 수행되는 경우 각각의 트랜잭션 연산에 간섭없이 독립적으로 수행되어야 한다.

✔️ 지속성 (Durability)

트랜잭션이 정상적으로 완료된 이후에는 데이터베이스 내에 작업이 영구적으로 반영되어 있어야 한다.

📈 트랜잭션 격리 레벨

0️⃣ Read-Uncommitted (트랜잭션 격리 레벨 0)

아직 커밋되지 않은 트랜잭션에서 처리중인 데이터를 다른 트랜잭션에서 읽는 것을 허용하는 격리 레벨로 데이터 정합성에서 문제가 많이 발생한다.

Dirty Read, Non-Repeatable Read, Phantom Read 이상현상이 모두 일어난다.

👉🏻 Dirty Read

하나의 트랜잭션이 다른 트랜잭션에서 커밋 이전에 변경한 데이터 대해서 조회를 해서 사용하지만, 데이터 변경을 수행한 트랜잭션에서 에러가 롤백을 하게 된 경우 최종적인 DB 형태는 업데이트 이전 데이터를 가지고 있을 것이다.

이때, 커밋 이전 변경한 데이터는 최종 DB에 적재된 데이터와는 다른 값을 갖는 문제가 발생하는데 이러한 이상현상을 “Dirty Read”라고 한다.

# DB MEMBER -> 1(ID), '맹'(NAME), 20(AGE)

BEGIN TRANSACTION A
SELECT AGE FROM MEMBER WHERE NAME = '맹';     # AGE = 20 -> Tran A
UPDATE MEMBER SET AGE = 30 WHERE NAME = '맹'; # AGE = 30 -> Tran A 
		BEGIN TRANSACTION B 
		SELECT AGE FROM MEMBER WHERE NAME = '맹';	# AGE = 30 -> Tran B
# 서버 에러로 COMMIT 되지 않고 Transaction A Rollback 
END

# DB MEMBER -> 1(ID), '맹'(NAME), 20(AGE)

A 트랜잭션에서는 해당 데이터를 Age = 30으로 업데이트를 하고, 커밋되기 이전에 B 트랜잭션이 Age를 조회했다. 이때 B 트랜잭션은 “30”이라는 값을 얻는다.

하지만, A 트랜잭션 수행 도중 서버 에러가 나서 Rollback을 한 경우 최종적으로 DB에 적재된 Age 값은 20이다.

따라서, B 트랜잭션에서 조회한 “Age=30”값은 잘못된 데이터이다.

image

1️⃣ Read-Commited (트랜잭션 격리레벨 1)

트랜잭션 처리중인 데이터에 대해서는 커밋 된 이후의 데이터만 조회할 수 있는 격리레벨로 대부분의 서비스에서 Default로 사용된다.

Non-Repeatable Read, Phantom Read 이상 현상이 발생한다.

👉🏻 Non-Repeatable Read

같은 트랜잭션 내에서 동일한 조회 쿼리를 수행한 경우에 조회하는 데이터가 다른 결과값을 갖는 이상현상을 “Non-Repeatable Read”라고 한다.

# DB MEMBER -> 1(ID), '맹'(NAME), 20(AGE)

BEGIN TRANSACTION A
SELECT AGE FROM MEMBER WHERE NAME = '맹';     # AGE = 20 -> Tran A
UPDATE MEMBER SET AGE = 30 WHERE NAME = '맹'; # AGE = 30 -> Tran A 
		BEGIN TRANSACTION B 
		SELECT AGE FROM MEMBER WHERE NAME = '맹';	# AGE = 20 -> Tran B
COMMIT; 
END
		SELECT AGE FROM MEMBER WHERE NAME = '맹'   # AGE = 30 -> TRAN B
		END;
# DB MEMBER -> 1(ID), '맹'(NAME), 30(AGE)

A 트랜잭션에서 Age 를 30으로 업데이트 하였다. B 트랜잭션은 격리레벨이 Read-Commited 단계이기 때문에 커밋하기 이전의 데이터인 “20”값을 조회한다.

이 후 , A 트랜잭션이 커밋을 완료한 후, B 트랜잭션이 동일한 쿼리로 Age를 다시 조회한 경우 조회되는 값은 “30”이다.

이 때, 같은 트랜잭션 내에서 동일한 쿼리로 데이터를 조회했을 때, Age값이 20 ≠ 30 인 경우가 발생한다.

image

2️⃣ Repeatable Read (트랜잭션 격리레벨 2)

트랜잭션 처리 이전에 커밋된 데이터에 대해서만 조회할 수 있는 격리레벨이다. 트랜잭션 내에서 읽어지는 동일 쿼리에 대해서는 항상 같은 데이터임을 보장한다.

Phantom Read 이상 현상이 일어난다.

👉🏻 Phantom Read

같은 트랜잭션 내에서 동일한 조회 쿼리를 수행한 경우에 이전 조회에는 없던 데이터가 이후 쿼리 조회에서는 나타나거나 이전 조회에 있던 데이터가 이후 쿼리 조회에서는 없어지는 이상현상을 “Phantom Read”라고 한다.

# DB MEMBER -> 1(ID), '맹'(NAME), 20(AGE)

BEGIN TRANSACTION A
SELECT * FROM MEMBER;     # 1(ID), '맹'(NAME), 20(AGE) -> Tran A
		BEGIN TRANSACTION B 
		INSERT INTO MEMBER VALUES (2, 'Zayson', 28);
		COMMIT;
		END
SELECT * FROM MEMBER;    # 1(ID), '맹'(NAME), 20(AGE) -> Tran A
END                      # 2**(ID), 'Zayson'(NAME), 28(AGE) -> Tran A**

# DB MEMBER -> 1(ID), '맹'(NAME), 20(AGE)
#           -> 2(ID), 'Zayson'(NAME), 28(AGE)

A 트랜잭션에서 Member 테이블을 전체 조회하는 쿼리를 처음 수행했을 때는 하나의 행만 조회되었다. 이후 동일한 쿼리를 수행하기 전에 B 트랜잭션에서 Member 테이블에 데이터를 입력하고 커밋했다.

이 경우 A 트랜잭션에서 동일한 Member 테이블을 전체 조회하는 쿼리를 수행하면, 처음 조회 시에 나온 결과인 한개의 행이 아닌 두개의 행에 대한 데이터가 조회된다.

image

3️⃣ Serializable (트랜잭션 격리레벨 3)

트랜잭션에서 사용중인 데이터를 다른 트랜잭션에서 접근할 수 없도록 하는 격리 레벨이다.

선행 트랙잭션이 데이터를 사용하고 있다면 해당 데이터에 Shared Lock을 걸어 다른 트랜잭션에서 Insert, Update, Delete 할 수 없도록 한다.

🚫 격리 레벨에 따른 이상 현상

Dirty ReadNon-Repeatable ReadPhantom Read
Read UncommitedOOO
Read CommitedOOX
Repeatable ReadOOX
SerializableXXX

📄 References

Interview_Question_For_Beginner (JBee님 깃헙 기술 인터뷰 정리) : https://github.com/JaeYeopHan/Interview_Question_for_Beginner/tree/master/Database

[DB 기초] 트랜잭션이란 무엇인가? : https://coding-factory.tistory.com/226

[데이터베이스] 트랜잭션 격리수준] : https://velog.io/@guswns3371/데이터베이스-트랜잭션-격리수준

[10분 테코톡] 🌼 예지니어스의 트랜잭션 : https://www.youtube.com/watch?v=e9PC0sroCzc

https://www.youtube.com/watch?v=e9PC0sroCzc

profile
백엔드 개발자 지망생입니다!

0개의 댓글