트랜잭션(Transaction)

여준서·2024년 10월 8일
post-thumbnail

트랜잭션은 데이터베이스에서 작업을 하느의 단위로 묶어 처리하는 개념으로 여러 작업을 하나의 트랜잭션으로 묶으면 모든 작업이 성공적으로 완료되거나 모두 실패해야합니다. 트랜잭션은 데이터의 일관성과 무결성을 유지하는데 중요한 역할을 하며, 데이터베이스가 신뢰할 수 있는 상태를 유지하는데 필수적입니다.

1. 트랜잭션의 핵심 속성(ACID)

트랜잭션은 ACID 속성을 만족해야 데이터 무결성을 보장할 수 있습니다.

1. 원자성(Atomicity)

트랜잭션 내의 모든 작업이 전부 완료되거나 전부 실패해야 한다는 성질입니다. 부분적으로 성공한 작업은 허용되지 않으며 중간에 오류가 발생하면 트랜잭션 전체가 롤백됩니다.

2. 일관성(Consistency)

트랜잭션이 완료되면 데이터베이스가 일관된 상태를 유지해야합니다. 트랜잭션 전후로 데이터베이스 상태는 제약 조건을 충족해야합니다.

3. 격리성(Isolation)

여러 트랜잭션이 동시에 실행되더라도 각 트랜잭션은 서로 간섭하지 않고 독립적으로 실행되어야합니다. 이를 통해 트랜잭션 간 데이터 충돌을 방지 할 수 있습니다.

4. 지속성(Durability)

트랜잭션이 성공적으로 완료되면 그 결과는 데이터베이스에 저장됩니다. 시스템 오류가 발생하더라도 커밋된 데이터는 보존됩니다.

2. 트랜잭션의 동작 과정

트랜잭션은 다음과 같은 단계를 거쳐 실행됩니다.

  1. 트랜잭션 시작 : 트랜잭션이 시작되고 여러 작업이 묶입니다.
  2. 작업 수행 : 트랜잭션 내에서 데이터베이스 연산(삽입,수정,삭제 등)이 수행됩니다.
  3. 커밋(Commit): 모든 작업이 성공적으로 완료되면 트랜잭션이 커밋되어 데이터베이스에 영구 저장됩니다.
  4. 롤백*Rollback) : 작업 중 오류가 발생하거나 트랜잭션을 취소해야할 경우 트랜잭션이 롤백되어 모든 변경 사항이 취소 되고 데이터 베이스는 원래 상태로 복구됩니다.

3. 트랜잭션 격리 수준

트랜잭션 격리 수준은 여러 트랜잭션이 동시에 실행될 때 트랜잭션 간에 어느 정도 영향을 주고 받을 지 결정하는 설정입니다. 격리 수준이 높을 수록 데이터 일관성은 보장되지만 성능이 저하될 수 있습니다.

1. READ UNCOMMITTED(읽기 미완료 허용)

가장 낮은 격리 수준으로 트랜잭션이 커밋도지 않은 데이터를 다른 트랜잭션에서 읽을 수 있는 상태입니다. 이로 인해 발생하는 문제를 더티 리드라고 하며 이는 트랜잭션 간에 데이터의 불일치 문제를 일으킬 수 있습니다.

더티 리드(Dirty Read)란?

  • 더티 리드는 한 트랜잭션이 아직 커밋되지 않은 데이터를 다른 트랜잭션에서 읽을 수 있는 상황을 말합니다.
  • 예를 들어 트랜잭션 A가 데이터를 수정했지만 아직 커밋하지 않은 상태에서 트랜잭션 B가 그 데이터를 읽는다면 트랜잭션 A가 롤백되었을 때 트랜잭션 B는 잘못된 데이터를 읽게 되는 문제가 발생합니다.

특징 :

  • 성능이 가장 좋지만 데이터 일관성이 보장되지 않기 때문에 신뢰할 수 없는 결과를 얻을 수 있습니다.
  • 더티리드,반복불가능한 읽기, 팬텀 리드와 같은 문제가 발생할 수 있습니다.

2.READ COMMITTED(읽기 완료 허용)

트랜잭션이 커밋된 데이터만 읽을 수 있도록 허용하는 격리 수준입니다. 즉 한 트랜잭션이 데이터를 수정하고 커밋하지 않은 상태에서는 다른 트랜잭션이 그 데이터를 읽을 수 없습니다. 더티 리드는 방지 할 수 있지만 반복 불가능한 읽기 문제는 발생할 수 있습니다.

반복 불가능한 읽기란?

  • 반복 불가능한 읽기는 한 트랜잭션이 같은 데이터를 두번 읽을때, 그 사이에 다른 트랜잭션이 데이터를 수정해 첫 번째 읽기와 두번째 읽기에서 다른 결과를 얻는 상황을 말합니다.
  • 예를 들어 트랜잭션 A가 데이터를 읽고 그 사이에 트랜잭션 B가 그 데이터를 수정하고 커밋하면 트랜잭션 A가 다시 데이터를 읽을 때 값이 달라질 수 있습니다.

특징 :

  • 더티 리드는 발생하지 않지만 반복 불가능한 읽기와 팬텀리드는 여전히 발생할 수 있습니다.
  • 많은 시스템에서 기본적으로 사용하는 격리 수준입니다.

3. REPEATABLE READ(반복 가능한 읽기)

트랜잭션 내에서 동일한 데이터를 여러번 읽어도 항상 같은 결과를 보장하는 격리 수준입니다. 즉, 트랜잭션이 시작된 시점의 데이터를 기준으로 읽기를 수행하며 다른 트랙잭션이 해당 데이터를 수정할 수 없습니다. 반복 불가능한 읽기 문제는 해결되지만 여전히 팬텀 리드 문제가 발생할 수 있습니다.

팬텀 리드란?

  • 팬텀 리드는 트랜잭션이 데이터를 읽는 동안 다른 트랜잭션에서 새로운 데이터를 삽입하거나 삭제하여 같은 쿼리를 실행했을 떄 결과에 변화가 생기는 문제입니다.
  • 예를 들어 , 트랜잭션 A가 특정 조건으로 데이터를 읽고 있을 때 트랜잭션 B가 그 조건에 해당하는 새로운 데이터를 삽입하면 트랜잭션 A가 같은 조건으로 다시 조회할 때 추가된 데이터(팬텀)를 보게 됩니다.

4. SERIALIZABLE(직렬화 가능

가장 높은 수준의 격리로 트랜잭션들이 완전히 직렬화된 것처럼 동작하도록 보장합니다. 즉,트랜잭션이 서로 완전히 격리되어 다른 트랜잭션이 동일한 데이터를 동시에 접근할 수 없습니다. 이 격리 수준에서는 더티리드,반복불가능한 읽기, 팬텀 리드 모두 방지 됩니다.

트랜잭션 격리 수준 비교

4. Spring에서 트랜잭션 관리

Spring은 @Transactional 어노테이션을 통해 트랜잭션을 간단하게 관리할 수 있습니다. 이 어노테이션을 붙이면 해당 메서드나 클래스 내의 모든 데이터 베이스 작업이 하나의 트랜잭션으로 처리됩니다.

 @Transactional
    public TodoSaveResponse saveTodo(AuthUser authUser, TodoSaveRequest todoSaveRequest) {
        User user = User.fromAuthUser(authUser);

        String weather = weatherClient.getTodayWeather();

        Todo newTodo = new Todo(
                todoSaveRequest.getTitle(),
                todoSaveRequest.getContents(),
                weather,
                user
        );
        Todo savedTodo = todoRepository.save(newTodo);

        return new TodoSaveResponse(
                savedTodo.getId(),
                savedTodo.getTitle(),
                savedTodo.getContents(),
                weather,
                new UserResponse(user.getId(), user.getEmail(), user.getNickName())
        );
    }
  • @Transactional : 트랜잭션 관리 어노테이션. 트랜잭션 내에서 오류가 발생하면 자동으로 롤백되고 , 그렇지 않으면 커밋됩니다.

마치며..

트랜잭션은 데이터베이스의 일관성무결성을 보장하기 위한 중요한 개념입니다. ACID 속성을 통해 트랜잭션이 안전하게 수행되며 , Spring 에서는 @Transactional을 통해 손쉽게 트랜잭션을 관리할 수 있습니다.

꼬리 질문 예상

1. 트랜잭션이 실패했을때는 어떻게 처리되나요?

  • 트랜잭션이 실패하면 롤백되어 데이터베이스는 트랜잭션 시작 전 상태로 복구됩니다. 이를 통해 데이터의 일관성을 유지할 수 있습니다.

2. 트랜잭션 격리 수준은 언제 어떻게 설정하나요?

  • 기본적으로 READ COMMITTED가 많이 사용됩니다. 하지만 동시성 문제가 우려될 경우 REPEATABLE READ나 SERIALIZABLE 같은 높은 격리 수준을 사용할 수 있습니다. 성능과 데이터 일관성 사이에서 적절한 수준을 선택해야합니다.

3. @Transactional을 메서드와 클래스에 동시에 사용할 수 있나요?

  • 가능합니다. 클래스에 @Transactional을 선언하면 해당 클래스의 모든 메서드에 적용되며 ,메서드에 별도로 @Transactional을 선언하면 해당 메서드가 우선 적용됩니다.

4. 트랜잭션 전파 속성(Propagation)이란 무엇인가요?

  • Propagation은 트랜잭션이 다른 트랜잭션과 어떻게 상호작용할지를 정의하는 속성입니다. 예를 들어, REQUIRED는 기존 트랜잭션이 있으면 참여하고 없으면 새로 시작합니다. REQUIRES_NEW는 항상 새 트랜잭션을 시작하는 방식입니다.
profile
DevOps 전향을 준비 중인 인프라 지향형 주니어 개발자

0개의 댓글