트랜잭션(Transaction) 에 대해 알아봅시다.

최동근·2022년 12월 16일
0

데이터베이스

목록 보기
2/5

데이터베이스를 공부하다보면 '트랜잭션(Transcation)' 이라는 단어가 빈번하게 나옵니다.
또한 스프링부트에서도 트랜잭션 기능을 사용하기 쉽게 제공하고 있습니다.
이번 글에서는 트랜잭션에 대해 깊이 알아보려고 합니다. 🚀

목차 📚

  • 트랜잭션(Transaction) 이란?
  • ACID
  • 스프링에서 트랜잭션 사용하는 방법

💡 1. 트랜잭션 이란?

트랜잭션(Transcation) 이란 여러개의 작업을 하나로 묶은 실행 유닛을 말하며,
데이터베이스 관점에서는 데이터베이스의 상태를 변환시키는 기능을 수행하기 위한 하나 이상의 쿼리를 모아 놓은 하나의 작업 단위를 말한다.

스프링 프로젝트를 진행하면 일반적으로 데이터베이스에 수 없이 많은 접근을 하게 됩니다.
스프링 MVC 패턴으로 작업을 하게 되는 경우, Controller 에서 적절하게 Service 단으로 실행 작업을 할당하게 됩니다. 실행 작업을 할당 받은 Service 단은 데이터베이스와 직접적으로 직면하는 레이어입니다.
그렇기 때문에, 데이터베이스 작업은 Service 단에서 통제 한다고 보면 될 것 같습니다.
(스프링 MVC 이해가 필요하다면 스프링 MVC 패턴에 대해 알아봅시다. 를 참고해 주세요!👀

이때 데이터베이스 관련 작업에는 서로 관련이 있는 것들이 존재하기 마련인데, 이를 하나의 단위로 묶어서 처리하는 것을 트랜잭션이라고 생각하면 됩니다.

  • 트랜잭션의 특징
    • 각 트랜잭션은 하나의 특정 작업으로 시작을 해서 묶여 있는 모든 작업들을 다 완료해야 정상적으로 종료한 다.
    • 만약 하나의 트랜잭션에 속해있는 여러 작업 중에서 단 하나의 작업이라도 실패하면, 이 트랜잭션에 속한 모 든 작업을 실패한 것으로 판단한다.
    • 작업이 하나라도 실패를 하게 되면 트랜잭션도 실패이고, 모든 작업이 성공적이면 트랜잭션 또한 성공이다.
    • 성공 또는 실패 라는 두 개의 결과만 존재하는 트랜잭션은, 미완료된 작업없이 모든 작업을 성공해야 한다.

💡 2. ACID

❗️ Atomicity (원자성)

-  All or Nothing 즉, 모든 작업이 실행되거나, 혹은 모든 작업이 실행되지 않아야 한다.
-  Commit or Rollback 

쉽게 말해, 하나의 단위로 묶여있는 여러 작업이 부분적으로 실행 된다면, 업데이트가 일어났지만 누가 업데이트했는지 모르거나, 업데이트 날짜가 누락되는 등 데이터가 오염될 수 있습니다.

예를 들어, 계좌이체를 하려는데 A 계좌에서는 출금이 이뤄지고, B 계좌에 입금되지 않았다고 가정합시다.
어디서 문제가 발생했는지 파악할 수 없다면, A 계좌에서 출금된 돈은 세상에서 사라지는 돈이 되는 꼴입니다.
만약 은행에서 이런 일이 발생한다면, 은행은 더이상 제 기능을 할 수 없을 것입니다.
A 계좌에서 출금하는 일에 성공했지만, B 계좌에 입금하는 작업에 실패한다면 계좌 A에서 출금하는 작업을 포함하여 모든 작업이 실패로 돌아가야 한다는 것이 Atomicity(원자성)입니다.
원자성을 지켰다면 1번과 2번, 두 작업이 모두 성공적으로 완료되어야 합니다.
그렇지 않으면(둘 중 하나의 작업이라도 실패한다면), 하나의 단위로 묶여있는 모든 작업이 실패하게 만들어 기존 데이터를 보호해야 합니다. (롤백 시킨다.)

❗️ Consistency (일관성)

- 데이터베이스의 상태가 일관되어야 한다는 성질이다.
- 모든 트랜잭션이 종료된 후에 DB의 제약 조건을 모두 지키고 있는 상태가 되어야 한다.

예를 들어, 일반적인 계좌의 잔액은 0원 보다 작을 수 없습니다.
따라서 해당 조건을 위반하는 트랜잭션은 모두 중단되어야 합니다.
(단, 트랜잭션 도중에 위배하더라도 결과과 정상적인 상태면 일관성을 가지는 것으로 간주합니다.)

❗️ Isolation (격리성)

- 트랜잭션 A 와 다른 트랜잭션들은 독립적으로 동작해야 한다.
- 서로의 작업을 알지 못하는 상태여야 한다.

쉽게 말해, 여러 트랜잭션 각각의 동작이 서로 영향을 끼치지 않으면서 독립적으로 동작해야 한다는 것입니다.
하지만, 현실에서는 완전한 격리성을 지키기가 어렵습니다.
완전한 격리성을 지향해서 설계를 하면 안정성 측면에서는 가장 좋지만 성능이 떨어지게 됩니다.(느린 DB 작업) 🚨

결국 안정성과 성능 측면이 Trade off 관계에 놓여 있기 때문에 4가지의 트랙잭션 격리 수준 이 존재하게
되는데 여기에는 (READ_UNCOMMITED, READ_COMMITED, REPEATABLE_READ, SERIALIZABLE) 이 있습니다.

트랜잭션의 격리 수준이란?
특정 트랜잭션이 다른 트랜잭션에서 변경하는 데이터를 볼 수 있도록 허용할지 말지를 결정하는 것
쉽게 얘기해서 격리 수준을 결정하는 행위는 각 트랜잭션의 격리 정도를 결정하여 안정성과 성능의 균형을 결졍하는 행위라고 할 수 있습니다.

⭐️ READ_UNCOMMITTED : 각 트랜잭션에서의 변경 내용이 COMMIT 이나 ROLLBACK 여부에 상관 없 이 다른 트랜잭션에서 값을 읽을 수 있도록 하는 격리 수준입니다.

  • 성능이 가장 좋은 방식이나, 안정성이 가장 떨어지는 방식입니다.(DIRTY READ 현상 발생 가능성)
    권장되지 않은 방식입니다. ❌
  • DIRTY READ 란 ? 특정 트랜잭션이 진행 중일 때 다른 트랜잭션에서 접근 하도록 열어두는 것 ->
    일관성이 맞지 않는 결과 도출

⭐️ READ_COMMITTED : 각 트랜잭션에서의 변경 내용이 성공적으로 마무리되어 COMMIT 이 되어야 다른 트랜잭션이 접근 가능 하도록 하는 격리 수준입니다.

  • 대표적으로 Oracle DB 에서 사용하고 있는 수준이며 가장 많이 사용되고
    있습니다.

  • DIRTY READ 현상이 발생하지 않습니다.

  • 커밋하기 전 다른 트랜잭션에서 조회시 UNDO 영역의 백업된 레코드에서 결과를 가져옵니다. (READ_COMMIITED 보장)
    (UNDO 영역 : 롤백을 대비해 변경 이전의 내용을 모두 기록하는 영역)

  • 문제점으로는, NON_REPEATABLE READ 문제가 발생 할 수 있습니다.
    < NON_REAPEATABLE READ 예시 >


⭐️ REPEATABLE_READ : UNDO 영역에 백업된 이전 데이터를 통해 동일한 트랙잭션 내에서는 동일한 결과를 보여주는 것을 보장하는 격리 수준 입니다.(MVCC) -> MVCC란? 를 참고해주세요!

  • 대표적으로 MySQL InnoDB 스토리지 엔진에서 주로 사용하고 있는 수준입니다.

  • 같은 트랜잭션 내에서의 반복되는 조회 대한 결과는 항상 동일해야 합니다.(NON_REAPEATALBE READ x)

  • UNDO 공간에 백업해두고 실제 레코드 값을 변경합니다.

  • 문제점으로는 PANTOM READ 문제가 발생할 수 있습니다.
    < PHANTOM READ 예시 >

    📣 트랜잭션 A 에서 수행한 변경 작업에 의해 레코드가 보였다가 안보였다가 하는 현상


⭐️ SERIALIZABLE : 가장 엄격한 격리 수준입니다. 동시에 성능이 가장 안좋은 트랜잭션입니다.

  • 데이터 베이스에서 거의 사용되지 않습니다.
  • 가장 단순한 격리 수준입니다.
  • PHANTOM READ 가 발생하지 않습니다.

❗️ Durability (지속성)

- 하나의 트랜잭션이 성공적으로 수행되었다면, 해당 트랜잭션에 대한 로그가 남아야 한다는 성질을 말합니다.
- 즉 트랜잭션 커밋 후, 데이터베이스에 영구적으로 보존되어야 합니다.

예를 들어 은행에서 계좌이체를 성공적으로 실행한 뒤에, 해당 은행 데이터베이스에 오류가 발생해 종료 되더라도 계좌이체 내역은 기록으로 남아야 합니다.
마찬가지로 계좌이체를 로그로 기록하기 전에 시스템 오류 등에 의해 종료가 된다면, 해당 이체 내역을 실패로 돌아가고 각 계좌들은 이전 상태로 돌아갑니다.

💡 3. 스프링 트랜잭션 사용하는 방법

profile
비즈니스가치를추구하는개발자

0개의 댓글