- 프로젝트에서 동시성 이슈를 다루면서 Transactional Serializable이 안되는지 이해가 되지 않았다.
- isolation level이 가장 높기 때문에 당연히 동시성 이슈가 해결될 것이라 생각했다.
- 하지만 비관적락에서만 동시성 이슈가 발생하지 않았다.
- 그래서 구체적인 동작방식을 이해하고자 정리했다.
프로젝트 동시성 이슈 발생
- 상황
- 예약 프로젝트이 특성 사 예약 요청 트래픽이 몰리는 상황을 테스트
- 100개의 스레드로 동시에 예약 요청하여 데이터를 생성
int threadCount = 100;
ExecutorService executorService = Executors.newFixedThreadPool(32);
List<NewReservationRequest> list = new ArrayList<>();
for (int i = 1; i <= threadCount; i++) {
ReservationPriceDto price = ReservationPriceDto.builder()
.productSeatScheduleId(1L)
.price(10)
.quantity(1)
.seatType(SeatType.S.name())
.placeId(31)
.build();
List<ReservationPriceDto> list1 = new ArrayList<>();
list1.add(price);
NewReservationRequest reservationRequest = NewReservationRequest.builder()
.memberId(1L)
.productId(1L)
.reservationPriceDtos(list1)
.build();
list.add(reservationRequest);
}
CountDownLatch latch = new CountDownLatch(threadCount);
// when
for (int i = 1; i <= threadCount; i++) {
executorService.submit(() -> {
try {
NewReservationRequest req = list.remove(0);
reservationService.createReservation(req);
} catch (Exception e) {
e.printStackTrace();
} finally {
latch.countDown();
}
});
}
latch.await();
// then
ProductSeatSchedule ret = productSeatScheduleRepository.findById(1L).get();
Assertions.assertThat(ret.getReservedQuantity()).isEqualTo(100);
Synchronized, Transactional Isolation, Pessimistic Lock 비교
- 데이터의 무결성을 보장하고 동시성 이슈를 해결하기 위한 방법
- 하지만 해결 방식이 다르다.
1. Synchronized
- 특징
- JVM에서 실행되는 코드의 동시성을 제어
- JVM에서 제공하는 스레드 동기화 메커니즘 (Montor Lock)
- 여러 스레드가 동일한 객체나 코드 블록에 동시에 접근하지 못하도록 함
- 메모리 내의 데이터 일관성을 보장
- 너무 많은 스레드가 하나의 모니터 락을 사용하려고 하면, 스레드 간 대기 시간이 길어져 성능이 저하될 수 있습니다.
- 적합 상황
- JVM 내부의 멀티스레드 환경
- 객체/코드 블럭 대상 공유 자원(메모리 데이터)의 동시 접근 방지
2. Transactional Serializable
- 동작 방식
- 범위 잠금(Range Lock)과 공유 잠금(Shared Lock)을 사용하여 데이터를 직렬화된 순서로 처리
- 읽기 작업조차 잠금을 설정하거나 범위 잠금을 걸기 때문에, 불필요한 잠금 대기가 발생 가능
- 트랜잭션 범위에 따라 자동 설정 (범위 잠금)
- (Pessimistic Lock 보다도) 과도한 잠금으로 동시성 처리량 감소
- 적합 상황
- 트랜잭션의 데이터 일관성 요구가 극도로 높은 경우.
- 테스트결과 DeadLock 발생
- 원인은 정규화로 나눈 여러 테이블에 대해 SELECT, UPDATE, CREATE 수행으로 여러 범위에 대해 락을 걸기 때문에 데드락 발생 가능성이 커진 것으로 추측
3. Pessimistic Lock
- 특징
- 개별 행(row) 또는 필요한 리소스에만 잠금
- 트랜잭션이 명시적으로 필요한 데이터만 잠그기 때문에, 과도한 잠금 범위를 방지하여 교착 상태 가능성을 줄임
- 필요한 데이터만 잠그므로 다른 트랜잭션이 불필요하게 대기하지 않음
- Pessimistic Lock은 개발자가 필요한 데이터만 잠그기 때문에 잠금 동작을 더 예측 가능하게 제어가능
- 적합 상황
- 특정 데이터에 동시 접근이 많은 경우
- 예약 좌석 현황에 대해 비관적 락을 걸어 여러 트랜잭션의 Phantom Read 현상을 방지하여 예약을 못하는 상황을 방지
/*
* 예매 시 동시성 이슈 방지
*/
@Override
@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<ProductSeatSchedule> findById(Long id);
...
- 테스트 통과
'CS > DB' 카테고리의 다른 글
InnoDB의 Buffer Pool, MVCC (0) | 2024.11.18 |
---|---|
MySQL) 타입 DateTime 과 Timestamp 중 어떤걸 쓸까 (0) | 2023.09.28 |
MySQL auto_increment와 innodb_autoinc_lock_mode (0) | 2023.06.12 |
mysql INFORMATION_SCHEMA 업데이트 속도 (0) | 2023.06.10 |
like 절 주의할 점 (0) | 2022.07.22 |