우아한 테코톡(13), 트랜잭션

2022. 10. 8. 12:10간단한 컴퓨터 이론/우아한테코톡

728x90
반응형

본 포스팅은 우아한Tech의 테코톡 샐리님의 트랜잭션 영상을 정리한 내용입니다.

트랜잭션의 개념과 특징, 속성을 위주로 정리했습니다.


트랜잭션

Q. 데이터베이스에 쿼리를 날릴 때 결과를 어떻게 신뢰할 수 있을까?

데이터베이스에서는 트랜잭션을 조작함으로서 사용자가 데이터베이스에 대한 완전성을 신뢰할 수 있도록 한다.

 

 

트랜잭션이란?

더 이상 나눌 수 없는 가장 작은 하나의 단위

 

모든 데이터베이스는 트랜잭션 지원하는데 하나의 명령을 실행했을 때 데이터 베이스가 온전히 그 명령을 실행할 수 있도록 돕는다. 데이터베이스는 트랜젝션을 관리할 수 있도록 한다. 데이터베이스에서는 명령을 끝마칠 때가지 수행내역을 로그에 반영한다. 데이터 베이스에 반영된 내용을 재반영하기 위한 redo log와 수행을 실패해 이전 상태로 되돌리는 undo log를 지원한다. 

 

Q. 데이터베이스에서 보장해주는 하나의 명령이 아닌 여러 명령을 하나의 트랜젝션에서 보고싶은 경우에는?

예를 들어 돈을 송금하는 경우 다음과 같은 과정이 필요하다.

  1. 돈을 보내는 사람의 잔고에 돈을 보낼만큼 충분한 금액이 있는지 확인한다.
  2. 돈을 보내는 사람의 잔고에서 금액을 차감한다.
  3. 돈을 송금한다.
  4. 돈을 받는 사람의 잔고에 돈을 추가한다.

 

위의 과정은 일부만 실행되어서는 안되고, 모두 함께 실행이 되거나 모두 취소되어야한다. 이러한 작업은 절대로 깨져서는 안되는 작업이기에 트랜잭션이라고 한다. 트랜잭션은 4가지 성질을 바탕으로 신뢰를 보장한다.

 

 

트랜잭션의 네가지 성질

원자성

일부만 실행되지 않고, 전부 다 실행되거나 전부 다 실패하는 성질

 

중간에 어떤 하나의 일을 실패했다면 중간에 진행했던 일을 없던 일로 되돌려야한다. 이를 롤백(Rollback)이라 한다. 반대로 하나로 묶여있는 모든 동작을 수행했으면 이를 데이터 베이스에 반영해야한다. 이를 트랜잭션 커밋(Commit)이라고 한다. 롤백이나 커밋이 수행된 뒤 트랜잭션이 종료된다. 

 

일관성

데이터베이스의 상태나 계층관계, 컬럼의 속성이 일관되게 유지되어야하는 성질

 

어떤 컬럼의 속성이 수정되면 트리거를 통해 일괄적으로 모든 데이터베이스에 적용해야한다.

 

지속성

트랜잭션이 커밋되었다면 어떤 문제가 발생하더라도 데이터 베이스에 그 내용이 영원히 지속되어야한다는 지속성이 있다. 로그를 남겨서 어떤 장애에도 대비할 수 있도록 한다. 

 

독립성

트랜젝션 수행 시 다른 트랜잭션 작업이 끼어들 수 없고, 각 트랜잭션이 독립적으로 수행되어야한다는 독립성이 필요하다. 각 다른 사람들이 동시에 돈을 송금해야할 경우에는 잔고를 조회하는 경우에 5만원으로 조회가 될 수 있다. 이때 송금했음에도 불구하고 송금한대로 증가하지 않는 경우가 발생할 수 있다. 따라서 독립성을 보장해 이러한 문제를 방지할 수 있다.

 

하지만 데이터 베이스에서 모든 작업을 독립적으로 수행하는 경우에는 수행시간이 느려져 비효율적이게 동작할 수 있다. 데이터 베이스에 저장된 데이터의 무결성과 동시성의 성능을 지키기 위해 트랜잭션의 설정이 중요해진다.

 

 

데이터베이스에서는 각각의 명령을 하나의 트랜젝션으로 보고 보장하기에 여러 명령을 트랜젝션으로 묶고 싶은 경우에는 개발자가 직접 트랜잭션의 경계를 설정해줘 트랜잭션을 명시하는 일이 필요하다. 스프링은 경계 설정을 데이터베이스에 전달할 수 있다. 스프링에서는 PlatformTransactionManager을 사용해 DataSource에 맞게 트랜잭션을 관리할 수 있게 하고 있다. 직접적으로 코드를 통해 구현하는 경우 외에도 AOP를 이용해 선언적 트랜잭션을 구현할 수 있다.

선언적 트랜잭션은 tx namespace를 사용하거나 어노테이션을 기반으로 설정할 수 있다.

 

 

tx namespace

Bean 설정 파일에서 트랜잭션 매니저를 등록하고 속성과 대상을 정의해 트랜잭션을 적용하겠다고 명시한다. 이렇게 적용하면 코드에 영향을 주지 않고, 일괄적으로 트랜잭션을 적용할 수 있다.

 

어노테이션 @Transactional

가장 많이 트랜잭션을 설정하는 방법으로 메소드, 클래스 등에 적용될 수 있다. 클래스에 대해 적용되면 모든 메소드가 트랜잭션에 적용된다. 중첩되어 존재하는 경우에는 클래스 메서드, 클래스, 인터페이스 메서드, 인터페이스 순으로 우선순위를 갖고 적용된다. 어노테이션이 적용된 메서드는 메서드 시작부터 트랜잭션이 시작되고 메서드가 끝나면 트랜잭션 커밋, 문제가 발생하면 롤백이 진행된다. 데이터 베이스에 여러번 접근하며 서비스 계층 메서드에 붙이는 것이 통상적이다. 코드에 일일히 붙이기 귀찮고, 귀찮은 단점이 있지만 세밀하게 표현할수 있다.

 

속성으로 지정이 가능하다. 

 

 

트랜잭션의 속성

트랜잭션은 몇 가지 속성을 가져 필요에 따라 추가할 수 있다.

 

트랜잭션의 propagation

전달받는 트랜잭션 전파

트랜잭션의 경계에서 이미 진행중인 트랜잭션이 있을 때 어떻게 동작할지 결정한다.

 

(1) REQUIRED

디폴트 설정인 REQUIRED가 지정된 메서드는 진행중인 트랜잭션이 없으면 새로 트랜잭션1로 시작하고, 트랜잭션이 있으면 메서드 2는 그 트랜잭션에 참여하게된다.두 메서드가 하나의 트랜잭션으로 실행되기에 어느 메서드에서 문제가 발생하면 실행한 메서드가 롤백된다. 

 

(2) supports

진행중인 트랜잭션이 있는 경우  required처럼 참여하고 트랜잭션이 없으면 트랜잭션 없이 메서드를 실행한다.

 

(3) Mandatory

진행중인 트랜잭션이 있으면 참여하고 없으면 예외처리된다. 혼자서는 메서드를 실행할 수 없다.

 

(4) Requires_new

항상 새로운 트랜잭션을 시작한다.

메서드 1은 트랜잭션 1이되고, 새로운 매서드 2가 시작되었을 때 진행중인 트랜잭션은 트랜잭션 1을 잠시 보류시키고 자신의 메서드인 메서드2를 트랜잭션으로 실행한다.

 

(5) not-supported

이미 시작된 트랜잭션이 있으면 보류하고, 자신의 메서드를 실행한다.

 

이는 트랜잭션을 사용하지 않는 설정이다.  메서드2를 실행하려는데 트랜잭션1이 있다면 트랜잭션1은 보류하고 메서드2를 실행한다.

 

 

(6) NEVER

트랜잭션을 사용하지 않도록 강제한다.

이미 진행중인 트랜잭션이 없다면 자신의 메서드를 실행하지만 트랜잭션이 있다면 예외를 발생한다.

 

 

(7) Nested

진행중인 트랜잭션이 있으면 새로운 트랜잭션을 만드는 설정이다.

 

트랜잭션 1이 진행중인데 메서드 2가 시작되었으면 트랜잭션 1 내부에 트랜잭션 2를 삽입하여 중첩한다. 트랜잭션 2는 부모인 트랜잭션1의 커밋과 롤백에 영향을 받지만 트랜잭션2의 커밋, 롤백에는 트랜잭션1이 영향을 받지 않는다. 

 

 

 

트랜잭션의 isolation

격리되는 트랜잭션 수준을 정할 수 있다. 동시에 여러 트랜잭션이 실행될 때 트랜잭션의 작업 내역을 다른 트랜잭션에게 보여줄지 말지를 결정하는 것으로 가능한 많은 트랜잭션을 동시에 진행하면서도 문제가 생기지 않도록 하려는 설정이다. 기본적으로 데이터베이스에 설정되어있지만 이 또한 설정할 수 있다. 격리 수준에 따라 락을 걸어서 데이터베이스 접근을 막는다.

 

 

(1) READ_UNCOMMITED

가장 낮은 격리수준으로 아직 커밋되지 않은 데이터를 다른 트랜잭션이 읽을 수 있는 설정이다.

 

이런 경우 트랜잭션 1 진행중에 트랜잭션 2가 읽어올 때 존재하지 않는 데이터를 읽어올 수 있다. 이 문제를 해결하기 위해 READ_COMMITED 격리 수준을 사용할 수 있다.

 

(2) READ_COMMITED

두번째로 낮은 격리수준으로 커밋되지 않은 데이터를 읽을 수 없다.

 

트랜잭션 1에서 작업이 완료되어 커밋되어야지만 트랜잭션 2는 A의 정보를 읽어올 수 있다. 하지만 같은 대상을 또 다른 트랜잭션이 수정할 수 있어  트랜잭션2가 다시 읽으면 값이 다르게 될 수 있다. 이는 REPEATABLE_READ를 사용해 해결할 수 있다.  대부분의 데이터베이스는 이를 디폴트 격리수준으로 따르고, 많이 사용한다. 

 

 

(3) REPEATABLE_READ

하나의 트랜잭션이 읽은 로우를 다른 트랜잭션이 수정할 수 없게 한다.

 

하지만 데이터베이스에서 데이터를 읽었는데, 새로운 로우가 추가되는 것이 제한되지 않아 다시 조회했을 때 발견하지 못한 새로운 로우가 발견될 수 있다. 

 

 

(4) SERIALIZABLE

동시에 같은 테이블의 정보에 접근할 수 없다.

 

이는 가장 높은 격리수준으로 위의 문제들을 모두 해결할 수 있다. 하지만 트랜잭션을 순차적으로 사용하는 것과 다를바 없어서 성능이 매우 떨어지니 극단적인 상황에서만 사용한다. 

 

 

timeout

트랜잭션 제한시간이 있다. 어노테이션이 달린 메서드를 수행하는데 일정 시간이 지나면 예외가 발생해 롤백된다. 따로 이 값을 설정하지 않으면 timeout은 지정되지 않는다. 

 

readOnly

true로 설정하면 UID 작업이 일어나는 것을 방지한다. 또한 이 모드를 적용하면 flush모드가 manual로 설정되어 jpa의 더티체킹 기능을 무시할 수 있다. 이는 성능 향상에 도움이 되기도 한다. 기본값은 false로 모든 작업을 허용한다. 

 

rollbackFor

전달받는 트랜잭션 롤백 예외로 체크 예외를 롤백으로 삼고 싶으면 이를 설정해 롤백할 수 있다. 

 

 

noRollbackFor

트랜잭션 커밋 예외 설정을 하는 속성으로 런타임 예외에 대해 롤백하지 않고 커밋하도록 할 수 있다.

 


출처

 

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

 

728x90
반응형