Spring/JPA

[Spring/JPA] 벌크 연산(Bulk Operation)과 EntityGraph

오잎 클로버 2023. 2. 13. 11:30
728x90

벌크 연산에 대해 설명하기 전에 DB 에 Member 라는 테이블이 있고,

구독 가입비(subscription_fee)와 등급(rate) 라는 칼럼이 존재하며,  구독 가입비 칼럼은 월 당 발생하는
  정기 구독 가입비, 등급 칼럼은 그 회원의 등급을 나타나며, 이는 구독 가입비에 영향을 미친다고 가정해 보자.

(등급은 일반(COMMON), 프리미엄(PREMIUM) 둘 뿐이다.)

 

등급이 일반(COMMON)인 모든 Member 의 subscription_fee 를 10% 만큼 인상한다면 다음과 같은 SQL문을 짤 수 있다.

UPDATE MEMBER SET SUBSCRIPTION_FEE = SUBSCRIPTION_FEE * 1.1 WHERE RATE = "COMMON";

이제 JPA 의 관점에서 생각해 보자. 아마, JPA 에서 특정 엔티티의 값을 변경하기 위해 다음과 같은 순서로 진행될 것이다.

  1. EntityManager#find 혹은 select 쿼리가 발생하여, 영속성 컨텍스트에 엔티티 저장 후 반환
  2. 반환받은 엔티티의 값을 변경한다. -> 영속성 컨텍스트에 반영된다
  3. Commit 시점에서 변경 감지(Dirty Checking)가 발생하며, update 쿼리가 나가며 DB 에 반영된다.

구독을 가입한 회원은 총 15만 명, 그중 등급이 일반인 회원이 만일 10만 명 존재한다고 가정해 보자. 

총 10만 번의 더티 체킹이 발생할 것이다. 즉, 10만 번의 update 쿼리가 발생한다는 것이다.

이러한 문제를 해결하고자, 벌크 연산이 등장하였다.

 

벌크 연산

벌크 연산은 UDPATE, DELETE 문을 지원하며, Hibernate 에서는 INSERT 문도 지원한다.

executeUpdate 메소드를 통해 벌크 연산을 수행한다. 여러 건의 수정 혹은 삭제를 한 번에 할 수 있다.

 

벌크 연산 주의 사항

※ 벌크 연산은 영속성 컨텍스트를 무시하고 DB 에 직접 쿼리를 전송한다는 점을 매우 주의해야 한다.

즉, DB 에 반영된 변경이 영속성 컨텍스트에 반영되지 않는 다는 것이다.

 

만약 회원의 등급이 COMMON 인 결과를 조회한 이후, 벌크 연산을 통해 수정을 하였다면,

영속성 컨텍스트에 있는 member.subscription_fee 와 DB 의 subscription_fee 의 값은 서로 다를 수 있다.

(영속성 컨텍스트와 DB 간의 데이터 차이 발생)

 

해결 방안

1. EntityManager#refresh 메소드 사용

벌크 연산 수행 이후, 정확한 엔티티를 사용해야 한다면, refresh 메소드를 사용하여, 
  DB 에서 엔티티를 다시 조회한다.

2. 벌크 연산 먼저 처리

벌크 연산을 先처리 後조회하면 된다. 가장 실용적인 해결책일 것이다.

3. 벌크 연산 수행 후 영속성 컨텍스트 초기화

영속성 컨텍스트에 남아있는 엔티티를 제거하는 방법이다.

 

요약

JPA 에서 단 건의 데이터의 경우 dirty checking 을 통해 UDPATE 를 수행하고,
  여러 건의 데이터의 경우 벌크 연산을 통해 한 번에 수정 혹은 삭제한다.

 

 

이상입니다.