[우아콘 2020] 수십억 건에서 QUERYDSL 사용하기 - YouTube

위 동영상에서 일괄 Update 최적화 부분에 대해 느낀 점을 작성합니다.

일괄 Update 최적화

아래 코드는 우리가 일반적으로 Jpa을 사용해서 user 정보를 update 하는 코드이다.

@Transactional
public void updateUserNames(List<Long> userIds, String newName) {
    for (Long userId : userIds) {
        User user = entityManager.find(User.class, userId);  // 각 엔티티 조회
        user.setName(newName);  // 엔티티 값 변경 (더티 체킹)
        // 트랜잭션 종료 시점에 변경된 모든 엔티티가 자동으로 업데이트

아래는 QueryDSL을 사용해서 userIds에 있는 모든 User 정보를 update 하는 코드이다. 

public void updateUsersStatus(List<Long> userIds, String newStatus) {
    QUser user = QUser.user;

    jpaQueryFactory.update(user)
        .set(user.status, newStatus)
        .where(user.id.in(userIds))
        .execute();
}

1만 건 단일 칼럼 기준 약 2000배 차이가 난다고 한다.(querydsl이 더 빠름)

Dirty Checking

더티 체킹(Dirty Checking)은 JPA(EntityManager 또는 Hibernate)가 관리하는 영속성 콘텍스트(Persistence Context) 내의 엔티티 객체들이 변경되었는지 감지하고, 트랜잭션이 끝날 때 변경된 엔티티를 자동으로 데이터베이스에 반영하는 메커니즘이다.

JPA는 엔티티의 상태를 관리하면서, 엔티티의 필드 값이 변경되면 이를 감지하고, 트랜잭션 종료 시점에 UPDATE 쿼리를 자동으로 생성하여 실행한다. 이를 통해 개발자는 데이터베이스 작업을 명시적으로 처리하지 않고도 간편하게 데이터를 수정할 수 있다.

왜 성능이 느릴 수 있는가?

  1. 변경 감지 비용: JPA는 엔티티의 변경 여부를 확인하기 위해 트랜잭션 동안 엔티티의 초기 상태(snapshot)와 현재 상태를 비교한다. 엔티티가 많아지거나 복잡해지면, 이 비교 작업이 성능에 부담을 줄 수 있다.
  2. 일괄 처리 불가: 더티 체킹은 일반적으로 개별 엔티티에 대해 작동한다. 다수의 레코드를 한 번에 수정해야 하는 경우, 일괄 업데이트를 사용하지 않으면 여러 개의 UPDATE 쿼리가 실행되어 성능이 저하될 수 있다.

느낀 점

무언가 Update를 할 필요가 있을 때, 더티 체킹에 의한 성능적 이슈를 고려해봐야 겠다고 느꼈다.