프로젝트 MVP 개발을 먼저 끝내고
본격적으로 리팩토링 작업에 들어갔다.
그러다가 무식하게 반복문으로 업데이트를 날리는 로직을 발견하였고,
이를 기점으로 쿼리 튜닝이 필요하다고 판단하였다.
이번에는 벌크 연산에 대해서 이야기 하려고 한다.
문제점
기존 코드는 아래와 같이
soft-delete 처리를 해준 후,
반복문으로 업데이트 처리를 해주고 있었다. (쿼리가 여러개 날라가서 비효율적이다)
//특정 게시글의 전체 댓글 삭제
public void deleteComments(String postId, String userId){
List<Comment> comments = commentRepository.findCommentAll(postId);
if(comments.isEmpty()){
throw new BaseException(NO_COMMENT_IN_POST);
}
for(Comment comment : comments) {
comment.softDelete(userId);
commentRepository.save(comment);
}
}
만약, 데이터의 수가 적다면 문제가 없겠지만,
수많은 데이터의 업데이트 연산이 발생한다면
굳이 테스트 해보지 않더라도
쿼리가 여러개 날라가면서
DB에 심각한 부하가 걸릴 것이다.
해결, 벌크연산 수행
그래서 쿼리를 여러번 날려서 업데이트를 수행하는 것 대신,
쿼리를 한번만 날려서 업데이트를 수행하는
벌크 연산을 구현하였다.
방법은 간단하다.
@Modifying과 @Query를 사용해 JPA가 제공하는 벌크 업데이트 기능을 활용했다.
( @Modifying 어노테이션은 excuteUpdate()-벌크연산 을 자동으로 수행해준다. )
@Modifying(clearAutomatically = true)
@Query("UPDATE Comment c set c.deletedBy = :userId, c.deletedAt = CURRENT_TIMESTAMP WHERE c.postId = :postId")
void softDeleteByPostId(@Param("postId") String postId, @Param("userId") String userId);
하지만 이렇게 벌크연산을 수행할 때는 고려해야 할 사항이 있다.
벌크 연산 전, 고려해야 할 점
벌크 연산이 수행되면, 영속성 컨텍스트를 거치지 않고 바로 DB에 쿼리를 날리기 때문에
영속성 컨텍스트의 데이터와 DB의 데이터가 일치하지 않아서
데이터 정합성이 깨지는 문제가 발생한다.
그래서 영속성 컨텍스트와 DB 데이터의 데이터가 불일치 하는 문제를 해결하기 위해서
벌크 연산 수행 후,
영속성 컨텍스트를 clear() or flush()를 해주면 된다.
나는,
clearAutomatically = true 를 통해서 clear()를 해주었는데,
flushAutomatically = true 를 통해서 flush() 도 가능하다.
벌크 연산을 수행하기 전에 데이터를 조회하고
벌크 연산이 수행 된 후의 DB에 있는 데이터와 비교하는 경우가 발생할 수 도 있다.
이 경우는 벌크 연산 후에 조회 연산을 하도록 처리해주어야 한다.
리팩토링 후
이렇게 리팩토링을 진행하였고,
@Transactional
public boolean deleteComments(String postId, String userId){
List<Comment> comments = commentRepository.findCommentAll(postId);
//댓글이 없을 경우, 삭제할 댓글이 없는 경우 이므로, true 반환
if(comments.isEmpty()){
return true;
}
//댓글 삭제가 성공할 경우, true 반환. 실패하면, false 반환
try{
commentRepository.softDeleteByPostId(postId, userId); //코드 간결해짐
return true;
}catch(Exception e){
log.error("댓글 삭제를 실패했습니다. postId: {}, userId: {}", postId, userId, e);
return false;
}
}
1. 벌크 업데이트는 단일 트랜잭션에서 처리되므로 트랜잭션 관리 비용이 줄어든다는 장점이 있고,
2. 반복문 대신 레포지토리 메서드 하나로 처리하여 코드 간결성과 가독성이 높아지고 유지보수가 쉬워진다.
'CS > 데이터베이스' 카테고리의 다른 글
댓글 대댓글 DB 설계하기 (0) | 2025.04.05 |
---|---|
DB를 선택하는 기준 (0) | 2025.04.05 |
캐싱 전략 (Chache-Aside, Write-Through, Write-Behind) (0) | 2025.03.07 |
HttpSession과 Session Clustering (with Redis) (2) | 2025.03.06 |
프로젝트에 Redis를 도입하는 근본적인 이유 (feat. 기술 면접) (0) | 2025.03.05 |