배경
회사에서 개발 중 엑셀 업로드에 관한 기능을 구현할 때였다.
요구사항은 새로운 엑셀 파일 업로드시 기존에 저장된 내용을 전부 삭제하고 새로운 엑셀 파일의 데이터를 DB에 저장한다는 것이었다.
당시에는 아무런 생각없이 데이터 전체삭제를 deleteAll()
을 사용했다.
문제는 얼마 뒤 발생했는데 엑셀 업로드시 처리하는 시간이 오래걸려 오류가 발생했다.
이런 현상에는 더 많은 복합적인 이유가 있지만 그 중 이번 포스팅에서는 deleteAll()
사용에 대해 알아보려한다.
왜 느려지는가?
deleteAll()
이라는 이름만 놓고보면 전체를 삭제해줄 것 같다.
하지만 내부적으로는 전혀 다르게 진행되는 걸 확인할 수 있다.
/*
* (non-Javadoc)
* @see org.springframework.data.repository.Repository#deleteAll()
*/
@Override
@Transactional
public void deleteAll() {
for (T element : findAll()) {
// 전체 엔티티를 하나씩 삭제한다.
delete(element);
}
}
내부적으로는 deleteAll()
호출 시에 전체 엔티티를 조회한 뒤 하나씩 삭제를 하고있다.
예를들어 약 10000개의 데이터를 deleteAll()
한다면 10000개의 데이터를 하나씩 삭제를 한다는 뜻이다.
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object)
*/
@Override
@Transactional
@SuppressWarnings("unchecked")
public void delete(T entity) {
Assert.notNull(entity, "Entity must not be null!");
if (entityInformation.isNew(entity)) {
return;
}
Class<?> type = ProxyUtils.getUserClass(entity);
// 엔티티를 삭제하기 위해 해당 엔티티를 조회한다...
T existing = (T) em.find(type, entityInformation.getId(entity));
// if the entity to be deleted doesn't exist, delete is a NOOP
if (existing == null) {
return;
}
em.remove(em.contains(entity) ? entity : em.merge(entity));
}
심지어 delete()
내부에서는 전달받은 엔티티를 한번 조회한 후 삭제 를 진행한다..
이로인해서 성능 이슈가 발생한 것이다.
해결 방법
이를 해결하기 위해서는 deleteAllInBatch()
메서드를 사용하는 방법과 직접 쿼리를 정의해 삭제하는 방법이 있다.
여기서는 deleteAllInBatch()
메서드에 대해서 알아보도록 한다.
/*
* (non-Javadoc)
* @see org.springframework.data.jpa.repository.JpaRepository#deleteAllInBatch()
*/
@Override
@Transactional
public void deleteAllInBatch() {
em.createQuery(getDeleteAllQueryString()).executeUpdate();
}
deleteAllInBatch()
메서드의 코드이다.
엔티티 매니저가 삭제 쿼리를 생성해 사용하는 로직이 구현되어있다.
때문에 일일히 엔티티를 검색해 삭제하는 deleteAll()
보다 훨씬 효율이 높다.
깨달은 점
사실 이런 문제는 이슈가 발생하기 전에 미리 고려할 수 있었다.
JPA를 사용하면서 발생하는 쿼리를 유심히 살펴보는 습관이 많이 부족하다는 걸 깨달았다.
좀 더 꼼꼼히 성능이나 발생할 오류등에 대해 고려하는 능력을 길러야겠다.
댓글