스프링의 @Transactional
트랜잭션
데이터베이스의 상태를 변환시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위 또는 한꺼번에 모두 수행되어야 할 일련의 연산들을 의미한다.
ACID
ACID는 데이터베이스 내에서 일어나는 하나의 트랜잭션(transaction)의 안전성을 보장하기 위해 필요한 성질이다.
원자성 (Atomicity)
- 트랜잭션의 연산은 데이터베이스에 모두 반영되든지 아니면 전혀 반영되지 않아야 한다.
- 트랜잭션 내의 모든 명령은 반드시 완벽히 수행되어야 하며, 모두가 완벽히 수행되지 않고 어느하나라도 오류가 발생하면 트랜잭션 전부가 취소되어야 한다.
일관성 (Consistency)
- 트랜잭션이 그 실행을 성공적으로 완료하면 언제나 일관성 있는 데이터베이스 상태로 변환한다.
- 시스템이 가지고 있는 고정요소는 트랜잭션 수행 전과 트랜잭션 수행 완료 후의 상태가 같아야 한다.
격리성 (Isolation)
- 둘 이상의 트랜잭션이 동시에 병행 실행되는 경우 어느 하나의 트랜잭션 실행중에 다른 트랜잭션의 연산이 끼어들 수 없다.
- 수행중인 트랜잭션은 완전히 완료될 때까지 다른 트랜잭션에서 수행 결과를 참조할 수 없다.
지속성 (Durablility)
- 성공적으로 완료된 트랜잭션의 결과는 시스템이 고장나더라도 영구적으로 반영되어야 한다.
BASE
NoSQL 데이터베이스 시스템에서 사용되는 데이터 일관성 모델입니다. ACID(원자성, 일관성, 격리성, 지속성)와 대조되는 개념으로, 분산 시스템에서의 유연성과 확장성을 강조합니다.
기본적인 가용성 (Basically Avaliable)
- 부분적인 고장은 있을 수 있으나, 나머지는 사용이 가능하다.
- 주 서버가 안되더라도 백업 서버는 동작한다.
소프트 상태 (Soft State)
- 노드의 상태는 외부에서 전송된 정보를 통해 결정됨.
- 분산 노드 간 업데이트는 데이터가 노드에 도달한 시점에 갱신.
- 최신 상태의 데이터로 덮어써진다.
결과적 일관성 (Eventually Consistent)
- 일시적으로 비일관적인 상태가 되어도 최적으로는 일관성이 있는 상태가 되는 성질
- 시스템 부하, 네트워크 속도 등의 외부 요인으로 인해 일관성이 일시적으로 깨질 수 있다.
JPA Isolation Level, Propagation
JPA @Transactional 에는 2가지 중요 옵션을 사용할 수 있다.
Isolation Level
은 DBMS에서 동시성 처리를 위해 설정하는 전략으로, 다른 트랜잭션에서 데이터를 읽을 때 데이터가 어떻게 보이는지를 결정합니다. 이를 통해 데이터의 일관성과 격리를 유지할 수 있습니다.Propagation
은 메서드나 함수 호출 시 트랜잭션을 시작하고, 이미 실행 중인 트랜잭션에 참여하거나, 새로운 트랜잭션을 시작하는 방법을 결정하는 것입니다. 이를 통해 트랜잭션의 범위와 동작을 관리할 수 있습니다.
격리 수준(Isolation Level)
트랜잭션 격리 수준을 설정하며, 속성을 설정하지 않은 경우에는 기본값으로 연결된 DB의 격리수준을 따릅니다.
READ UNCOMMITTED (읽기 비공유)
- 가장 낮은 격리 수준입니다.
- 한 트랜잭션이 아직 커밋되지 않은 데이터를 다른 트랜잭션이 읽을 수 있습니다 (더티 리드).
- 동시성은 높지만, 데이터 일관성 문제가 발생할 수 있습니다.
READ COMMITTED (읽기 공유)
- 대부분의 RDBMS의 기본 격리 수준입니다.
- 한 트랜잭션이 커밋된 데이터만 다른 트랜잭션이 읽을 수 있습니다.
- 트랜잭션 내에서 동일한 쿼리를 두 번 이상 실행했을 때, 첫 번째 실행 시에 읽은 데이터와 두 번째 실행 시에 읽은 데이터가 다른 현상을 말합니다 (non-repeatable read)
REPEATABLE READ
- 처음에 읽은 데이터의 스냅샷을 트랜잭션 동안 유지하여 동일한 쿼리를 실행할 때 일관된 결과를 제공합니다.
- 그러나 새로운 행이나 기존의 행이 추가되거나 삭제되는 경우에는 이러한 보장이 깨질 수 있어 새로 추가되거나 제거된 값을 가져올 수 있습니다. (Phantom read)
- MySQL의 InnoDB 스토리지 엔진의 기본 격리 수준입니다.
SERIALIZABLE
- 가장 높은 격리 수준입니다.
- 트랜잭션이 완전히 격리되어, 동시에 다른 트랜잭션이 같은 데이터에 접근할 수 없습니다.
- 더티 리드, 논리피트 리드, 팬텀 리드가 모두 방지됩니다.
- 동시성이 크게 감소하며, 성능에 영향을 줄 수 있습니다.
격리 수준(Isolation Level)이 낮을 경우 발생할 수 있는 문제
더티 리드 (Dirty Read)
- 설명: 한 트랜잭션이 아직 커밋되지 않은 데이터를 다른 트랜잭션이 읽는 현상입니다.
- 문제점: 롤백될 수 있는 미완성 데이터를 읽기 때문에, 이후 데이터의 일관성이 손상될 수 있습니다.
논리피트 리드 (Non-Repeatable Read)
- 설명: 한 트랜잭션 내에서 같은 데이터를 두 번 읽을 때, 두 번의 읽기 사이에 다른 트랜잭션이 해당 데이터를 수정하거나 업데이트하면, 두 번의 읽기 결과가 서로 다를 수 있습니다.
- 문제점: 트랜잭션의 일관성이 깨지며, 예측 불가능한 결과를 초래할 수 있습니다.
팬텀 리드 (Phantom Read)
- 설명: 한 트랜잭션 내에서 동일한 쿼리를 두 번 실행할 때, 첫 번째 쿼리와 두 번째 쿼리 사이에 다른 트랜잭션이 해당 쿼리의 결과 집합에 영향을 미치는 새로운 데이터를 삽입하거나 삭제하는 경우입니다.
- 문제점: 동일한 쿼리의 결과가 일관되지 않아, 데이터 무결성에 문제가 발생할 수 있습니다.
하지만 격리 수준은 동시에 여러 트랜잭션이 있을 때 동일한 데이터를 읽을 수는 있도록 하지만, 특정 데이터에 대한 동시 변경에 따른 충돌이 발생할 때 이를 해결할 수 있는 전략을 제공하는 것은 아닙니다.
그래서 JPA에서는 이보다 세밀하고 높은 수준의 격리 수준이 필요할 때 낙관적락 과 비관적락 개념을 사용합니다.
전파 속성(Propagation)
트랜잭션 전파(propagation) 속성은 하나의 트랜잭션 내에서 여러 개의 메서드 호출이 있을 때, 각 메서드 호출이 동일한 트랜잭션 내에서 수행될지 또는 새로운 트랜잭션을 시작할지를 결정하는 방법을 제어합니다.
전파 속성은 7가지로 아래와 같습니다.
REQUIRED
- 호출된 메서드가 이미 존재하는 트랜잭션 내에서 실행됩니다. 존재하지 않는 경우에는 새로운 트랜잭션을 시작합니다.
REQUIRES_NEW
- 호출된 메서드가 항상 새로운 트랜잭션을 시작합니다. 이미 존재하는 트랜잭션이 있더라도 새로운 트랜잭션을 시작하고, 호출된 메서드가 실행을 완료하면 이전의 트랜잭션 상태를 저장하고 새로 시작한 트랜잭션으로 변경합니다.
MANDATORY
- 호출된 메서드가 반드시 이미 존재하는 트랜잭션 내에서 실행되어야 합니다. 존재하지 않는 경우에는 예외가 발생합니다.
NESTED
- 호출된 메서드가 이미 존재하는 트랜잭션 내에서 실행되거나, 새로운 중첩 트랜잭션을 시작합니다. 중첩 트랜잭션은 부모 트랜잭션에 속하며, 부모 트랜잭션과 독립적으로 커밋 또는 롤백될 수 있습니다.
SUPPORTS
- 호출된 메서드가 이미 존재하는 트랜잭션 내에서 실행됩니다. 존재하지 않는 경우에는 트랜잭션 없이 실행됩니다.
NOT_SUPPORTED
- 호출된 메서드가 트랜잭션 없이 실행됩니다. 이미 존재하는 트랜잭션이 있더라도 일시 중단됩니다.
NEVER
- 호출된 메서드가 반드시 트랜잭션 없이 실행되어야 합니다. 이미 존재하는 트랜잭션이 있을 경우에는 예외가 발생합니다.
MVCC (Multi-Version Concurrency Control)
MVCC는 여러 트랜잭션이 동시에 같은 데이터에 접근할 때, 데이터의 일관성과 동시성을 보장하는 방식입니다. 이 기술의 핵심은 잠금(Locking) 없이 데이터를 일관되게 읽는 것이며,
InnoDB 스토리지 엔진은 이를 구현하기 위해 언두 로그(Undo log)를 활용합니다.
MVCC 의 특징
- lock 기법만 적용된 DBMS보다 훨씬 빠르게 동작한다.
- 사용하지 않는 데이터가 매번 생기므로 데이터를 정리하는 시스템이 필요하다.
Mysql innoDB, PostgreSQL에서 사용하고 있다.
Db Lock
보통 mysql에 기본 격리레벨은 Repeatable read이다 그러나 해당 격리레벨로도 동시성을 해결할 수 없는경우에 jpa 비관적락을 사용해 동시성을 제어한다 이때 사용되는 락이 베타락, 공유락 이다
공유락(Shared Lock)
- 여러 트랜잭션이 동시에 데이터를 읽을 수 있지만, 동시에 해당 데이터를 수정할 수 없도록 하는 락입니다.
- 다수의 트랜잭션이 데이터를 읽는 동안 공유락을 획득하게 되면, 다른 트랜잭션이 해당 데이터를 읽을 수 있지만 수정은 할 수 없습니다.
베타락(Exclusive Lock)
- 특정 데이터에 대한 읽기 및 쓰기 권한을 가진 트랜잭션이 단독으로 접근할 수 있도록 하는 락입니다.
- 베타락을 획득한 트랜잭션은 다른 트랜잭션이 해당 데이터에 대해 공유 락 또는 베타락을 획득하는 것을 막습니다. 즉, 베타락을 가진 트랜잭션은 데이터를 읽고 수정할 수 있으며, 다른 트랜잭션은 해당 데이터에 접근할 수 없습니다.