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

JunHyeon님의 프로필 이미지
JunHyeon

작성한 질문수

자바 ORM 표준 JPA 프로그래밍 - 기본편

벌크 연산

여러대 서버에서 사용하는 엔티티가 벌크 연산되는 경우 질문 드립니다.

작성

·

473

20

아래와 같은 상황인 경우에, 어떤방식으로 엔티티들의 영속성을 유지할수 있을까요?

- JPA 를 사용하는 API 서버 10대

- JPA 를 사용하는 BATCH 서버 2대

상황

- API 서버 몇개에서, BATCH에서 업데이트 될 엔티티가 영속 상태

- BATCH 서버에서 벌크 연산 수행

위와 같은 경우에 API 서버에서 엔티티들을 어떻게 관리해야하는지 궁금합니다.

답변 1

33

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

안녕하세요 SW09님^^

실무를 많이 경험해보신 분의 좋은 질문이네요.

결론적으로 여러가지 좋은 방법이 있지만, JPA가 제공하는 낙관적 락(@Version)을 사용하면 됩니다.

이 문제는 A 유저와 B 유저가 동시에 같은 데이터를 수정하는 단순한 문제로 정의할 수 있습니다.

예를 들어 A 유저가 상품의 가격을 봤을 때는 1000원 이었는데, 이 가격을 1500원으로 수정하려는 순간, B 유저가 같은 상품의 가격을 2000원으로 갱신하고, 커밋까지 완료해버렸습니다.

그런데 이후에 A 유저는 상품의 가격이 2000원으로 변한 것을 모르고, 1500원으로 수정을 하고, 커밋을 완료하게 됩니다.

결과적으로 상품의 최종 가격은 2000원은 무시되고, 1500원이 됩니다.

이것을 두 번의 갱신 분실 문제(second lost updates problem)이라 합니다.

쉽게 이야기해서 마지막에 커밋한 내용이 인정된다는 것이지요.

이렇게 마지막에 커밋한 내용을 인정하면 아무 문제가 없습니다.

문제는 누군가 중간에 내가 수정하려는 데이터를 변경해버렸을 때, 그것을 감지하는 것이 어렵지요.

결국 다음과 같이 3가지 선택지가 있습니다.

- 마지막 커밋만 인정하기

- 최초 커밋만 인정하기

- 충돌하는 갱신 내용 병합하기

최초 커밋만 인정하기나, 충돌하는 갱신 내용 병합하기는 결국 A 유저가 상품을 조회한 1000원 부터 A 유저가 상품을 1500원으로 변경할 때 까지 누군가 데이터를 중간에 변경하면, 변경 되었다는 사실을 알아야 합니다.

그래야 A의 커밋을 포기할지(최초 커밋한 B의 커밋만 인정), 아니면 사용자 UI 같은 것에 경고를 띄워주어서 누군가 1000원의 상품을 2000원으로 변경했으니 가격을 다시 수정해주세요 라고 표기할지(충돌하는 갱신 내용 병합하기) 선택할 수 있습니다.

JPA는 이 문제를 해결하기 위해 낙관적 락(@Version)이라는 메커니즘을 제공합니다.

쉽게 이야기해서 엔티티와 테이블에 버전 필드를 하나 만들어서 @Version 어노테이션으로 매핑하면 됩니다.

JPA는 엔티티가 갱신될 때 마다 자동으로 버전을 체크하면서 동시에 버전을 증가하기 때문에 충돌을 인식할 수 있습니다. 만약 충돌이 발생하면 JPA는 낙관적 락 예외를 터트립니다.(OptimisticLockException)

추가로 벌크 연산을 수행할 때는 수동으로 이 버전 필드의 값을 직접 하나 증가시켜주면 됩니다.

제가 최대한 쉽게 설명해드리려고 노력은 했는데, 락 문제는 단순하지 않고, 상황에 따라 적절한 해결 방안이 다릅니다. 그리고 잘못 적용하면 장애로 이어질 수 있습니다. 더 깊고 자세한 내용은 자바 ORM 표준 JPA 프로그래밍 책 16장 트랜잭션과 락 부분을 한번 보시길 권장드립니다.

감사합니다^^

JunHyeon님의 프로필 이미지
JunHyeon

작성한 질문수

질문하기