<aside>
💡
251105(수) 배송 담당자 생성 시 동시성 문제에 대하여
문제 사항
배송 담당자를 생성할 때 순번이 부여되는데, create가 동시에 일어나면 같은 순번이 부여될 수 있는 문제가 발생
문제 정의 및 해결 과제
배송 담당자 생성시 순번이 부여되기 때문에, DB에서 마지막 순번을 확인한 후, 마지막 순번+1을 부여하여 담당자를 생성해여야 한다. 만약 배송 담당자 생성이 동시에 일어났을 때, 조회된 마지막 순번이 같으므로, 똑같은 순번의 번호로 insert시도가 동시에 일어나게 된다. 이때 중복 데이터가 발생하거나, 한쪽의 요청이 실패하게 되는데, 이것을 어떻게 해결해야 할까?🧐
전략
- DB 레벨에서 제약조건 + 재시도
- 비관적 락(Pessimistic Lock) —
SELECT ... FOR UPDATE
- 별도 시퀀스 테이블 / Redis INCR
선택한 전략 및 해결 과정
Step 1. 1번 (Unique 제약조건 + 재시도)
- 배달기사는 총 200명 내외, 데이터 생성 시도가 빈번하지 않을 것으로 예상
- MSA + 단일 DB = 트랜잭션은 DB 단위로 안전함
- 서비스는 분리되어 있어도 DB가 하나면 DB 레벨의 유일성 제약 (UNIQUE CONSTRAINT) 은 여전히 전역적으로 유효
- 즉, 여러 서비스에서 동시에 insert해도 DB가 중복된
(type, order)를 막아줌
- 비관적 락은 불필요한 오버헤드
SELECT ... FOR UPDATE 같은 비관적 락은 트랜잭션이 동시에 걸릴 때마다 대기해야 함
- 200명 중 1명이 가끔 새로 생기는 정도의 빈도라면 락으로 인한 대기 시간은 낭비
- 비관적 락은 초당 수십~수백 요청이 몰리는 경우(예: 결제, 주문번호 생성)에만 이득이 있음
- Redis 시퀀스는 오버엔지니어링
- Redis나 별도 시퀀스 테이블은 고성능 분산 환경(수천~수만 TPS)에서 의미가 있음
- 지금 같은 소규모, 단일 DB 기반 MSA라면 관리 포인트만 늘어나고 복잡도만 증가
- 유지보수성 측면에서도 “DB로만 관리되는 순번”이 더 명확하고 추적 가능
Step 2. 1번을 선택했으나 다중스레드에서 시도시 동시성 문제를 처리하지 못하여 2번 시도
- 여러 스레드가 거의 동시에
MAX(delivery_order)+1 값을 조회하고 insert하면, 동일한 순번을 동시에 생성하려 시도
- DB 유니크 제약조건이 있으니 결국 한쪽은 DuplicateKeyException이 발생해서 실패.
- 즉, 중복을 막는 건 가능하지만 Deadlock이나 실패 자체는 막을 수 없음.
- 해결 과정
- 배송담당자 ID + 배송타입 + hubID 조합의 unique 제약조건 추가.
- 쿼리메서드에 @Lock 추가 후
@Lock(LockModeType.*PESSIMISTIC_WRITE*)
- 서비스에서 배송담당자 생성 충돌시 5번까지 재시도하도록 로직 변경
- 테스트 수행 (총 3개의 멀티 스레드 환경에서 시도 )

“현재 허브에 등록된 배송담당자 중 가장 큰 배송순번(delivery_order)을 찾아서 다음 순번을 만들기 전에 락을 걸고 읽겠다” 라는 의미
성공

📌 아쉬운 점
- 10개의 스레드에서 시도했을 땐 60%정도의 성공률을 보였다. 더 안전하게 개선하고 싶었으나, 트랜잭션과 동시성에 대한 이해가 부족해서 리팩토링하기 어려웠다. 좀 더 공부한 후 다양한 테스트를 시도해보고 보완해봐야 겠다.
</aside>