인프런 커뮤니티 질문&답변

kim님의 프로필 이미지
kim

작성한 질문수

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발

id질문드립니다

해결된 질문

작성

·

499

0

id값으로도 검색을 용이하게 하고 싶으면

auto_increment를 사용하는것보다

달마다 2020030000001식으로

2020 년 3월이 되면 저런식으로 초기화해서 +1

4월에는 20200400000001

식이 되게해서 사용하고픈데 어떻게해야할까요?

그리고 이런식으로 초기화된후에 +1하는 로직은

동시성 문제 이슈가 없을려면 오떻게 해야 할까요

답변 11

0

kim님의 프로필 이미지
kim
질문자

네 감사합니다~ ^^

0

김영한님의 프로필 이미지
김영한
지식공유자

아쉽게도, 넘버만을 위한 api서버를 두고 api서버에 있는 db를 분리하더라도. 문제가 있습니다^^!

주문 서버에서 동시에 주문번호를 요청하면, 넘버만을 위한 api 서버가 아무리 많아도 db isolation이 serialize면 api 들이 순차적으로 실행됩니다.

결국 동시에 여러 요청이 오면 모든 쓰레드들이 대기하게 됩니다.

성능까지 고려해서 이런 문제를 해결하려면 동시성 관련해서 많은 부분을 깊이있게 공부해야 합니다.

성능 부분이 크게 문제가 안되면, 처음부터 데이터베이스에 락을 걸고 문제를 해결하면 됩니다.

(이 부분은 데이터베이스 락 부분을 공부해보시길 권장합니다.)

성능 부분이 크게 문제가 되는 상황이라면, 앞서 말씀드린 JPA의 낙관적 락 메커니즘을 공부해보시면 도움이 되실거에요^^

감사합니다.

0

kim님의 프로필 이미지
kim
질문자

혹시 넘버만을 위한 api 서버를 두고 그 api서버를 지나서 create요청이 가게 어떨까 생각이 듭니다 api서버에 있는 db는 isolation을 serialize로 두면 동시성 문제는 안생길거고, 실제 주문에 대한 요청만 넘버링이 되기 때문에 성능상에도 문제가 없을껏 같아서요. 실제 쇼핑몰을 이용하다 보면은 내가 원하는 상품까지 리스트를 불러와서 주문하기까지가 서버렉이이걸리지 실제로 주문하기 클릭하고나서는 서버렉은 잘 안 생겼던것 같아서요

0

김영한님의 프로필 이미지
김영한
지식공유자

kim님 안녕하세요^^

사실 이 동시성 문제가 참 어렵습니다.

말씀해주신 시나리오대로 진행하게 되면

A사용자와 B사용자가 정말 동시에 요청을 하게 되면

1. A도 51로 업데이트 하고 B도 51로 업데이트 할 수 있습니다.

이 문제를 풀려면 update value=value+1 과 같은 식으로 하면 1번 문제는 해결은 됩니다.

그런데 그 다음에 51로 업데이트 요청을하고나서 51값을 가져오는 구간이 문제입니다.

이 시점에 다른 사용자들이 정말 동시에 들어오면 A가 조회하는 값이 51이 아니라 52나 53일 수도 있습니다.

A,B가 동시에 들어오면

1. A가 51로 업데이트

2. B가 52로 업데이트

3. A가 조회하면 52

4. B가 조회하면 52

이렇게 될 수 있는 것이지요.

이 부분에 대한 해결책으로, 참고할 만한 부분은 JPA의 낙관적 락을 사용하는 방식을 공부해보시면 도움을 얻을 수 있습니다.

JPA책 16장에서 트랜잭션과 락에 대해서 설명하는 부분에 JPA의 낙관적 락 메커니즘이 자세히 설명되어 있으니 참고해보시면 도움이 될 것 같습니다^^

감사합니다.

0

kim님의 프로필 이미지
kim
질문자

아 그러면은 번호를 가져올 때 업데이트 -> 셀렉트로 진행하면 될 것 같습니다. 일단 새로 여청이 오면은 51로 먼저 업데이트를 하고 51값을 가져오는 것입니다. 넘버는 의미가 없는 유니크한 속성만 갖면 되기 때문에 중간에 번호를 가져가고 에러가 나서 실제 51을 사용은 안되도 문제가 생기지는 않기 때문입니다. 먼저 업데이트를 했기때뮨에 먼저 시작한 트랜잭션이 번호를 가져감을 보장할 수 있습니다

이렇게 되면 동시성도 해결이 가능할 거 같다고 생각이 듭니다. 

0

김영한님의 프로필 이미지
김영한
지식공유자

저는 다음과 같이 테이블을 만들어서 구현하면 좋을 듯 합니다.

필드: 날짜, 시퀀스

(2020-01, 0)

(2020-02, 0)

(2020-03, 0)

원하는 날짜가 달이면 달을 기준으로 미리 만들어두고, 일이면 일을 기준으로 미리 만들어 두는 것이지요.

그래서 1월에 100건 2월에 50건이면 다음과 같이 나올거에요

(2020-01, 100)

(2020-02, 50)

(2020-03, 0) -> 아직 2월

이렇게 구현한다고 가정했을 때, 시나리오를 다시 써 주시겠어요?

0

kim님의 프로필 이미지
kim
질문자

처음부터 번호 테이블에 값을 많이 생성하거나

sequence 전용 테이블을 만들어서 배치로 매달 1일마다 초기화할 것 같습니다

0

김영한님의 프로필 이미지
김영한
지식공유자

많이 고민하셨군요^^!

우선 1번을 해결하고 그 다음으로 가는게 좋을 것 같아서 추가 질문을 드릴께요

1) 저번달 주문을 기준으로 3배정도의 번호를 테이블에 미리 준비해둠

-> 만약 요구사항이 저번달 주문보다 수백배(예측 불가) 많은 주문이 다음달에 발생할 수 있다면 구현 방법을 어떻게 변경하시겠어요?

0

kim님의 프로필 이미지
kim
질문자


1. ID생성 방법
1) 저번달 주문을 기준으로 3배정도의 번호를 테이블에 미리 준비해둠
ex) 저번달 주문이 100개면 1~300개 정보를 미리 만들어둠
Table order_num
num | used | complete
00001 N N
00002 N N
00003 N N
2)서버로 Create요청이 올 때 prefix로 localdatetime을 이용해서 yyyymm 만듬
3)order_num 테이블에서 used='N' and complete='N' 이면서 제일 앞의 번호를 가져오게 하고
used='y'업데이트함
이유)
4) prex + num으로 주문 id생성(202003000001)
5) 정상적으로 주문이 되었으면 order_num의 complete='Y'로 업데이트함
(주문번호까지는 만들어졌어도 중간에 실패하면 complete가 업데이트가 되지 않아
에러가 발생했음을 알 수 있고, num을 가져올 때도 계속 다름 번호로 순차적으로 가져올 수 있음)
6) 다음달 (4월)이 되면은 00시가 되면 order_num의 테이블 값을 1 , N , N 으로 초기화해서
다시 000001 부터 가져올 수 있게 함
2. 동시에 create요청이 올 경우
1) order_num이라는 테이블을 이용하기 때문에 transaction에 맡김
transaction isolation은 기본값(commtted read)으로 놔두고 DB에 맞김
(똑같은 시간에 create요청이 와도 db transaction은 순서대로 생성될 거 같다고 생각이 되는데
이 부분은 어떻게 되는지 알려주시면 감사하겠습니다.)
일단 제가 생각한 방법입니다 ^^;;
인터넷에서 찾을 때는 hibernate를 이용한 커스텀 id를 생성하는 방법만 보여서
여태까지 공부해왔던 것을 토대로 한번 생각해보았습니다.

0

kim님의 프로필 이미지
kim
질문자

넵 오늘 알아보고 답변드리겠습니다 ㅎㅎ

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. kim님^^ 좋은 질문입니다.

여러가지 해결방안이 있을 수 있는데요. 제가 바로 말씀드리면 더 많은 것을 얻어가실 수 없으니 반대로 질문을 드릴께요^^

(해답을 찾아가는 과정속에서 더 많이 배울 수 있으니까요)

애플리케이션 서버(WAS)는 2대 이상 확장될 수 있다고 가정하고, kim님이 생각하는 나름의 방법을 찾고 고민해주시면, 제가 그 방법의 장점과 단점을 말씀드릴께요^^

다양하게 고민하고, 자료도 찾아보시고 답변 주세요^^

kim님의 프로필 이미지
kim

작성한 질문수

질문하기