스프링 프레임워크는 @Transactional 어노테이션을 제공하여 트랜잭션을 지원합니다. @Transactional의 옵션 중 isolation level 옵션은 무엇이고, 어떠한 경우에 필요한 개념인지 뱅킹 프로세스에 자주 사용되는 입출금 예제를 기반으로 설명하고자 합니다.
Isolation Level
일반적으로 어플리케이션에서 서비스가 실행 되면 프로세스가 생성되고 각각 쓰레드가 할당 됩니다. 이러한 쓰레드들이 동시에 같은 서비스에 접근하여 SELECT , UPDATE 등의 작업을 공유하게 되었을 경우 예외적인 경우가 발생합니다. 이러한 문제를 해결하기 위하여 격리 수준(Isolation Level)을 지정하며 쓰레드의 작업, 즉 트랜잭션을 어느 정도까지 공유할 것인지 정의 해주는 것이 해당 옵션입니다. Isolation Level은 크게 4가지로 분류됩니다.
- READ UNCOMMITTED
- READ COMMITTED
- REPEATABLE READ
- SERIALIZABLE
READ UNCOMMITTED
READ UNCOMMITTED는 COMMIT 여부에 상관없이 모든 트랜잭션이 자원을 공유하는 레벨로 트랜잭션의 가장 낮은 격리 수준입니다. 해당 레벨로 설정하게 될 경우 위와 같은 ROLLBACK 상황에서 잘못 된 데이터를 읽게 되는데 이를 Dirty Read(더티 리드)라고 합니다. 스프링에서는 아래와 같이 작성하여 적용합니다.
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
READ COMMITTED
READ COMMITTED는 COMMIT된 데이터만 읽어오겠다는 설정으로 트랜잭션A 내에서 UPDATE가 이루어지더라도 COMMIT 되지 않았다면, 트랜잭션B는 트랜잭션A의 COMMIT 이전 시점의 데이터를 읽어옵니다. 해당 옵션을 사용하게 되면 DIRTY READ 문제를 해결할 수 있지만 동시성 문제를 해결하지 못합니다. 위와 같이 COMMIT 되기 전 트랜잭션 B가 접근하게 된다면, 트랜잭션A의 작업이 끝나고 트랜잭션B의 작업이 종료되는 경우가 발생하여 이전 작업이 덮어씌워지게 될 수 있습니다. 그 외에도 READ COMMITTED에 생길 수 있는 문제로는 Phantom Read와 Non-Repeatable Read 문제가 발생하는데 이 부분은 자세히 다루지 않도록 하겠습니다.
@Transactional(isolation = Isolation.READ_COMMITTED)
REPEATABLE READ
REPEATEABLE READ는 트랜잭션 내에서 항상 동일한 조회를 보장하는 설정으로 Non-Repeatable Read 문제가 발생하지 않는 격리 수준입니다. 이는 동일한 트랜잭션에서 재조회시 조회 내용을 보장해주는 것이지만, 위와 같은 동시성 문제를 해결하지는 못합니다.
@Transactional(isolation = Isolation.REPEATABLE_READ)
SERIALIZABLE
SERIALIZABLE은 SELECT가 사용되는 모든 데이터에 트랜잭션이 끝날때까지 Shared Lock(공유 락)을 걸어 동기 처리를 지원하는 레벨로 트랜잭션 A와 트랜잭션 B가 동시에 실행된다면 완전한 격리 수준으로써 트랜잭션A가 종료되고 트랜잭션B가 실행될 수 있습니다. 이러한 레벨을 사용하면 동시적인 처리에 대해서 해결할 수 있지만, 동기 처리를 지원하기 때문에 트래픽이 많은 서비스에서는 속도를 보장해주지 못할 수 있습니다.
@Transactional(isolation = Isolation.SERIALIZABLE)
정리
지금까지 계좌입출금 예제를 통해 동시성 처리를 위주로 트랜잭션 격리 수준에 대해 정리해보았는데요. 실무에서 isolation 옵션을 사용하면서 개발자로써 중요한 내용이라는 생각이 들어 글을 작성하게 되었습니다. 입출금을 예제로 든 이유는 이 업무를 해보고 싶어 예제로 정리하면 괜찮지 않을까 하여 주제로 정하게 되었습니다. 현업에서는 이러한 입출금 트랜잭션 처리를 어떻게 하는지 궁금하며, 혹시 실무를 경험한 분들이 비슷한 처리 경험을 공유해주신다면 감사하겠습니다. 부정확한 내용이 포함되어 있는것 또한 댓글로 남겨주시면 수정하도록 하겠습니다. 앞으로도 공유하고 싶은 지식이 있을시 글로 정리하고자 하며 많은 분들이 봐주셨으면 좋겠습니다.
'Spring' 카테고리의 다른 글
[Spring] @Transactional 없이 트랜잭션을 보장 받는 방법 - AOP 활용 (4) | 2024.01.03 |
---|---|
[Springframework]JavaMailSender text/html로 안보내질때 해결 방법 (0) | 2023.08.30 |
[SpringBoot] 메시지 국제화 적용하기 (0) | 2023.08.13 |