0%

주문 번호 생성하기 1(feat. MySQL 재현)

주문 번호

주문 번호
커피 전문점에서 주문 번호는 중요한 역할을 합니다. 주문 번호는 주문을 구별하는데 사용하고 주문한 고객을 부르는데 사용합니다. 또한 사장님에게는 당일 몇 번의 주문이 들어왔는지 직관적으로 알려주는 역할도 합니다.

요구 사항

  • 주문 번호는 매일 초기화 됩니다.(참고)
  • 주문 번호는 가게별로 다릅니다.
  • 같은 날 같은 같은 가게에는 동일한 주문 번호가 없습니다.
  • 주문 번호는 1부터 시작해서 1씩 증가합니다.

최초 시도

synchronized를 통해서 주문 번호를 생성하는 메서드를 동기화하였습니다.
그러면 각 주문 번호를 순차적으로 생성할 수 있을 것이라고 생각했습니다.

문제 발생

문제 발생
5s가 차이나도 동일한 주문 번호가 생성되는 문제가 발생했습니다.

문제 원인

메서드를 호출하는 매서드에서 @Transactional을 사용하고 있습니다. 그래서 @Transactional이 하위 전파되어 주문 번호 생성 매서드에 @Transactionalsynchronized을 동시에 적용됐습니다. 그 결과 race condition이 발생했습니다.

이는 MySQL의 격리 수준이 REPEATABLE READ이기 때문에 발생한 문제입니다.

MySQL 재현

MySQL 재현
T(트랜잭션) 시작 -> count -> count + 1을 insert -> commit을 하는 코드를 2개 준비합니다.

spring boot환경과 동일하게 T1 시작 -> T2 시작 -> ~~ -> T1 commit -> T2 commit 순서로 실행하면서 어떤 결과가 나오는지 확인해보겠습니다.

T1, T2을 시작한 후 T1count결과 입니다.
MySQL 재현

T1count + 1insertcommit까지 해줍니다.
MySQL 재현

T2count결과 입니다.
MySQL 재현
하지만 T2count결과는 여전히 2입니다.

T2count + 1insertcommit까지 해줍니다.
MySQL 재현

최종 결과를 보면 동일한 order_number가 생성되는 것을 확인할 수 있습니다.
MySQL 재현

해결 방법

order_number_sequence를 생성하고 가게마다 주문 번호를 관리했습니다. 그리고 주문 번호를 생성할 때는 비관적 락을 사용하여 race condition을 방지했습니다.

이제 매일 주문 번호를 0으로 초기화 해야하는데 이는 lambda를 사용하여 해결할 수 있습니다. AWS Lambda로 SQL 배치 작업 하기

결과

결과
5s가 차이나도 같은 주문 번호가 나오던 것이 1.4s의 차이가 생겨도 다른 주문 번호가 나오는 것을 확인할 수 있습니다.

고찰

개인적으로 DB를 활용해서 해결하는 것을 좋아하지 않습니다. 따라서 DB를 활용하지 않고 해결하는 방법을 현재 시도중에 있습니다. 2편을 기대해주세요.