자바 주니어 개발자로 이제 8개월 차가 되었다.
현재 유지보수를 맡고 있는 기존 웹 서비스와, 새롭게 메인으로 담당하게 된 심평원 연계 서비스를 운영하고 있다.
심평원 연계 서비스는 전체 시스템 중 일부이지만, 그중에서도 핵심이 되는 중요한 서비스다.
이번 포스트에서는 해당 서비스 중 하나인 파일을 읽어 DB에 insert할 때 발생한 지연 문제와, 이를 어떻게 개선했는지에 대해 정리해보려고 한다.
해당 기능을 실행하는 데 시간이 과도하게 소요되었다.
→ 명확한 성능 문제가 존재했다.
기존 로직은 리스트를 이중 반복문으로 비교하며 중복을 제거하는 구조였다.
for (int i = 0; i < list.size(); i++) {
for (int j = 0; j < list.size(); j++) {
if (i != j && list.get(i).equals(list.get(j))) {
// 중복 제거
}
}
}
insert 과정에서도 이상한 점을 발견했다.
| 구간 | 소요 시간 |
|---|---|
| 초반 | 약 2분 |
| 후반 | 약 9분 |
→ 뒤로 갈수록 점점 느려지는 현상 발생
문제는 크게 두 가지였다.
Set을 활용하여 중복 제거
Set<Dto> set = new HashSet<>(list);
List<Dto> distinctList = new ArrayList<>(set);
시간복잡도
O(n²) → O(n)
대량 데이터에서 체감 성능 크게 향상
for (int j = 0; j < list.size(); j++) {
dao.save(list.get(j));
}
Hibernate의 영속성 컨텍스트 누적
flush() / clear() 수행for (int j = 0; j < list.size(); j++) {
dao.save(list.get(j));
if ((j + 1) % 100 == 0) {
dao.flush();
dao.clear();
}
}
dao.flush();
dao.clear();
flush
clear
👉 반드시 flush + clear를 함께 사용해야 의미 있음
기능적으로는 문제가 없었지만,
데이터가 많아지자 구조적인 문제가 드러났다.
차이는 단순하지만 결과는 매우 크다.
이 구조를 이해하지 못하면 성능 문제가 발생한다.
대량 데이터 처리에서는 “구조”가 성능을 결정한다.