이번에 특정 조건을 충족한 유저들에 대해 컬럼 값을 update 해주는 일배치를 만들게 되었는데요, 그 과정에서 성능 개선을 고민했던 내용을 간략하게 작성해보려 합니다.
대충 상황을 요약해보자면
간략하게는 이러한 상황인데, 저는 ItemProcessor에서 값을 변경할 때, 변경감지가 동작하기 때문에 JpaItemWriter에서는 별도의 작업을 하진 않았고, 그대로 변경된 값들이 flush되게끔 두었습니다.
결과적으로는 모든 값이 정상적으로 변경되었습니다.
근데 문제는, 예를들어 1000건의 유저에 대해 값을 변경해서 flush()를 했을 때, update 쿼리가 1000개가 나가는 상황이 발생했습니다.
yaml 파일에서 batch_size 옵션을 변경해주면 된다고 하는데, update는 동작을 안하더라구요.. (insert는 되는 것 같음)
변경해야할 값은 달랐지만, 동일한 컬럼을 update하는 상황이었기 때문에, 값을 기준으로 묶어서 update를 시키면 될 것 같았습니다.
ex) update 테이블 set 변경할 컬럼 = 값 where user_id in (1,2,3....)
따라서 기존에는 영속화된 Entity의 프로퍼티 값을 수정하고, 변경감지로 update 쿼리를 발생시켰지만, JPQL을 사용한 native query를 작성하게 되었습니다.
@Modifying
@Query("UPDATE 테이블명 SET 컬럼명 = :value WHERE userId IN (:userIds)")
fun 함수명~~
대충 이런식이었던 것 같습니다.
@Modifying 어노테이션을 붙여야 데이터를 조작하기 위한 DML임을 감지한다고 합니다. 안쓰면 에러가 발생한다고 합니다.
사실 변경 감지를 활용한 코드가 좀 더 간결하고 깔끔하며, 이 변경이 성능에 얼마나 도움이 될지는 모르겠습니다.
하지만, 이전에는 JPA를 무작정 최선의 선택이라고 생각했었는데, 아닌 상황이 있다는 것을 알 수 있었습니다.
예를들어, 위 상황에서 JpaItemWriter가 아닌 JdbcBatchItemWriter를 사용한다고 하면, batchUpdate를 수행해주기 때문에 이와 같은 복잡한 작업 없이도 쿼리를 한방에 모아서 발생시킬 수 있습니다.
하지만 협업하는 동료가 위 프로젝트의 메인 역할을 담당하고 있는데, JPA를 사용하는 것을 결과적으로 원하고 있어서, 그에 맞춘 최선의 방법을 찾아보았습니다.
앞으로도 틈틈이 리팩토링을 진행할 것이기 때문에, 혹시 또 다른 좋은 방법이 있으면 기록할 생각입니다.
Spring Batch는 처음이라 아직도 잘 모르겠고, 어색하지만 이런 고민을 통해 점점 알게되는 것 같고, 요즘 너무 재밌네요 ㅋㅋㅋ