티스토리 뷰
문제 상황
유저 정보를 업데이트하는 API를 확인해 보자.
이 API는 @PatchMapping을 활용해 특정 칼럼(birth, intro, nickname, profileImage)만 업데이트하는 경우이지만
실제 UPDATE 쿼리를 확인하면 모든 컬럼에 대해 set이 들어가 있다..?
2024-08-23T22:04:40.768+09:00 DEBUG 14232 --- [moongge] [nio-8080-exec-5] org.hibernate.SQL :
/* <criteria> */ select
ue1_0.userId,
ue1_0.badgeList,
ue1_0.birth,
ue1_0.fcmToken,
ue1_0.group_code,
ue1_0.intro,
ue1_0.nickname,
ue1_0.password,
ue1_0.profileImage,
ue1_0.userName,
ue1_0.userType
from
UserEntity ue1_0
where
ue1_0.userId=?
2024-08-23T22:04:41.883+09:00 DEBUG 14232 --- [moongge] [nio-8080-exec-5] org.hibernate.SQL :
/* update
for com.narsha.moongge.entity.UserEntity */update UserEntity
set
badgeList=?,
birth=?,
fcmToken=?,
group_code=?,
intro=?,
nickname=?,
password=?,
profileImage=?,
userName=?,
userType=?
where
userId=?
업데이트를 하는 로직을 확인해보면
@Override
public UserProfileDTO updateProfile(String userId, MultipartFile multipartFile, UpdateUserRequestDTO updateUserRequestDTO) {
UserEntity user = userRepository.findByUserId(userId)
.orElseThrow(() -> new UserNotFoundException(ErrorCode.USER_NOT_FOUND));
...
// 유저 정보 업데이트
user.updateProfile(updateUserRequestDTO.getBirth(),
updateUserRequestDTO.getNickname(),
updateUserRequestDTO.getIntro(),
imageUrl);
return UserProfileDTO.mapToUserProfileDTO(user);
}
첫 번째 쿼리인 SELECT 문은 유저 정보를 가져오기를 위한 쿼리,
그리고 우리가 봐야 하는 두 번째 쿼리인 UPDATE 문은 JPA의 변경 감지로 업데이트를 진행하고 있다.
그렇다면 왜 모든 칼럼에 대해 set이 들어가 UPDATE 쿼리가 나가는지 보자.
문제 원인
JPA의 구현체 하이버네이트는 결국 JDBC API를 내부적으로 사용하는데, JDBC에서는 Statement와 PreparedStatement라는 두 가지 방법으로 쿼리를 실행할 수 있습니다.
Statement는 쿼리를 실행할 때마다 쿼리문 분석 -> 컴파일 -> 실행의 과정을 반복합니다.
반면, PreparedStatement는 처음 이 과정을 거친 후 쿼리를 캐시에 저장하여 재사용합니다.
하이버네이트는 쿼리 재사용과 성능 최적화를 위해 주로 PreparedStatement를 사용하며,
쿼리에서 '?' 부분만 바꿔가면서 효율적으로 작업을 처리합니다.
문제 해결
특정 칼럼만 업데이트를 날리고 싶다면 @DynamicUpdate를 활용할 수 있는데,
@DynamicUpdate 어노테이션을 엔티티에 붙여주면 됩니다.
@DynamicUpdate
public class UserEntity {
...
}
UPDATE 쿼리를 확인해 보면
2024-08-23T22:26:08.110+09:00 DEBUG 17984 --- [moongge] [nio-8080-exec-3] org.hibernate.SQL :
/* update
for com.narsha.moongge.entity.UserEntity */update UserEntity
set
birth=?,
intro=?,
nickname=?,
profileImage=?
where
userId=?
변경된 부분만 set이 설정되어 UPDATE문이 나가는 것을 볼 수 있다.
BUT...
이렇게만 놓고 보면 무조건 사용하는 것이 좋은 거 아닌가? 생각할 수 있지만
@DynamicUpdate 에 대해 찾아보면 경고하는 블로그의 글을 많이 찾아볼 수 있다. 그 이유를 알아보자.
위에서 "PreparedStatement는 처음 이 과정을 거친 후 쿼리를 캐시에 저장하여 재사용합니다. " 라고 했는데
SQL 구문을 캐시하고, ? 로 작성된 파라미터 부분만 변경해 가면서 재사용하게 됩니다.
2024-08-23T22:04:41.883+09:00 DEBUG 14232 --- [moongge] [nio-8080-exec-5] org.hibernate.SQL :
/* update
for com.narsha.moongge.entity.UserEntity */update UserEntity
set
badgeList=?,
birth=?,
fcmToken=?,
group_code=?,
intro=?,
nickname=?,
password=?,
profileImage=?,
userName=?,
userType=?
where
userId=?
@DynamicUpdate를 활용할 때는, 엔티티가 업데이트할 때마다 SQL문을 만들어, 이런 SQL 캐시 히트율이 떨어지게 됩니다.
또 JPA 입장에서도 추가적인 연산이 필요해집니다.
모든 칼럼을 수정할 때는 엔티티 객체의 변경에 대해서만 추적하면 됐지만
@DynamicUpdate를 실행하기 위해서는 변경된 컬럼을 찾기 위해서 필드 수준의 추적이 필요해지기 때문입니다.
오히려 전체 칼럼을 하나의 PrepareStatement 스타일의 SQL을 반복해서 사용하는게, 더 속도가 빠를 수도 있다는거죠.
즉, @DynamicUpdate 성능 오버헤드를 가지고 있고, 실제로 필요할 때만 사용을 해야 한다!
그럼 언제 사용해?...🤔
1. 엔티티가 많은 컬럼을 가지고 있을 때
가장 단순하고 기본적인 이유라고 볼 수 있는데, 칼럼이 많거나, 칼럼의 크기가 크거나, 길이가 너무 길거나 사용할 수 있다. 물론 "많다" 라고 하는 것은 추상적인 양이므로 잘 판단해야 할 것 같음.
++ '자바 ORM 표준 JPA 프로그래밍' 책에서는 성능 향상 기준은 컬럼 30개이상 이라고 함.
2. 테이블에 인덱스가 많을 때
많은 칼럼을 가지고 있는 경우와 비슷한 이유인데, 칼럼이 변경이 발생할 경우 인덱스 재정렬을 하게 됩니다.
인덱스가 많으면 그만큼 리소스가 많이 소모된다.
++ 근데 데이터가 너무 크다면 이미 테이블을 분할했지 않았을까...
👉🏻 물론 다른 다양한 이유가 있을 수도 있다. @DynamicUpdate의 사용 유무는 개인별로 상황에 맞게 결정하는 것이 좋을 듯해 보인다
++ 내 경우는 많은 양의 필드도 아니거니와 적은 양으로 필드를 업데이트를 하는 경우가 아니므로 @DynamicUpdate을 사용하지 않기로 결정했다.
Reference
https://rudaks.tistory.com/entry/spring-data-jpa%EC%9D%98-DynamicUpdate
spring data jpa의 @DynamicUpdate
1. 개요 Hibernate로 Spring Data JPA를 사용할 때, @DynamicUpdate와 같은 추가적인 특징을 사용할 수 있다. @DynamicUpdate는 JPA 엔터티에 적용될 수 있는 클래스 수준의 어노테이션이다. 이는 변경된 컬럼에 대
rudaks.tistory.com
https://multifrontgarden.tistory.com/299
@DynamicUpdate 는 언제 써야할까
JPA 를 이용하면 엔티티의 상태를 변경해주는 것만으로 update 쿼리를 실행시키게 된다. 이때 발생하는 쿼리는 모든 컬럼을 대상으로 update 를 실행한다. @Entity @Table(name = "person") public class Person { @Id
multifrontgarden.tistory.com
https://www.yes24.com/Product/Goods/19040233
자바 ORM 표준 JPA 프로그래밍 - 예스24
자바 ORM 표준 JPA는 SQL 작성 없이 객체를 데이터베이스에 직접 저장할 수 있게 도와주고, 객체와 관계형 데이터베이스의 차이도 중간에서 해결해준다. 이 책은 JPA 기초 이론과 핵심 원리, 그리고
www.yes24.com
'TIL' 카테고리의 다른 글
OneToOne 관계에서의 N + 1 문제 (0) | 2024.09.07 |
---|---|
JPA에서 save할 때 select 쿼리가 나가는 이유? (0) | 2024.08.23 |
Docker란? (0) | 2024.08.19 |
쿠키, 세션, 토큰 어떤 차이일까? (0) | 2024.04.23 |
- JPA
- 메일
- @MappedSuperclass
- @TransactionalEventListener
- 1차 캐시
- @OneToMany
- 즉시 로딩
- onetoone
- @joincolumn
- @Table
- 준영속
- 조인 전략
- 연관관계
- N + 1
- @Entity
- Redis
- 스키마 자동 생성
- 인메모리 db
- mappedBy
- 영속성 컨텍스트
- @ManyToOne
- 비동기
- 변경감지
- 비영속
- @Id
- 단일 테이블 전략
- 최적화
- @GeneratedValue
- @Cacheable
- 엔티티 매니저