트랜잭션 격리 수준이 필요한 이유
트랜잭션 수준 읽기 일관성 (Transaction-Level Read Consistency)을 지키기 위함이다.
격리수준에는
4가지
가 있다.- READ_UNCOMMITED (level 0)

- READ_COMMITED (level 1)

- REPEATABLE_READ (level 2)

- SERIALIZABLE (level 3)
격리 수준이 높아질수록 동시성(Concurrency)은 높아지고 속도는 느려진다.
JPA를 사용하게 되면 트랜잭션 격리 수준이 READ COMMITTED 정도가 된다.
낙관적 락
낙관적 락은 이름 그대로 트랜잭션 대부분은 충돌이 발생하지 않는다고 낙관적으로 가정하는 방법이다. 이 경우 데이터베이스가 제공하는 락 기능 대신 JPA가 제공하는 버전 관리 기능을 사용한다.
쉽게 말하자면, 어플리케이션이 제공하는 락이라고 할 수 있다.
- 낙관적 락은 트랜잭션 커밋 전까지는 트랜잭션의 충돌을 알 수 없다는 특징이 있다.
비관적 락
비관적 락은 이름 그대로 트랜잭션간 충돌이 발생한다고 가정하여 우선 락을 거는 방법이다.
데이터베이스가 제공하는 락 기능을 사용하는데, 대표적으로 select for update 구문이 있다.
두번의 갱신 분실 문제
만약 사용자 A와 사용자 B가 동일한 게시물을 수정한다고 해보자.
두 명이 동시에 수정 화면을 열어 내용을 수정중인데, A가 먼저 수정을 마쳤다고 해보자.
해당 게시물의 내용은 A가 수정한 내용을 반영하지만, 이 때 B가 수정을 마치면 B의 내용으로 덮어씌워진다.
이를 두 번의 갱신 분실 문제 라고 한다. 왜냐하면, A가 수정한 내용이 분실되기 때문이다.
우리가 서비스를 이용할때 위와 같은 상황을 겪으면 어떨까?
물론 위와 같은 상황이 올바르다고 생각할 수도 있다. 하지만 사용자 A의 입장에서는, 이를 오류로 받아들이지 정상적인 상황으로 받아들이기는 어려울 것이다.
어쩌면, B가 작성을 완료할 때 A의 수정이 적용된 것을 확인한 서버가 예외를 발생시켜서, "최근 수정이 이루어져 해당 동작을 처리할 수 없습니다" 라는 알림을 주는 것이 더 올바른 방법일수도 있다.
일단 두 번의 갱신 분실 문제를 해결하는 방법은 다음 세 가지가 있다.
- 마지막 커밋만 인정하기: B의 내용만 인정하고 A의 내용은 무시한다.
- 최초 커밋만 인정하기: A의 내용을 인정하고, B가 수정 시 오류를 발생시킨다.
- 충돌하는 내용 병합하기: A와 B의 내용을 병합시킨다.
세 번째 솔루션은 특수한 상황이 아니라면 선택하기 어려울 것이고,
아무래도 우리는 두 번째 솔루션에 눈길이 간다.
JPA는 이를 위해, 버전 관리 기능을 제공한다. ( 낙관적락)
MonthSub
우리 서비스에서는 READ COMMITTED + 낙관적락을 사용할 것이다.
비관적락을 쓸 이유는 아직 없다고 생각하여 추후 낙관적락으로 처리 불가능할 경우 비관적으로 바꿀 예정이다.
출처