507개의 데이터를 넣는데 2199124ms -> 약 36분 40초의 시간이 소요되었습니다.
1개의 브랜드 데이터를 넣을때 약 4.3초가 소요됩니다.
12000개를 넣는다면 약 61시간 38분으로..심각합니다.
jpa:
properties:
hibernate:
order_inserts: true
order_updates: true
batch_versioned_data: true
jdbc:
batch_size: 500
// 가맹점 평균매출 저장
if (dto.getFranchiseAvgSales() != null) {
for (Map.Entry<String, Map<String, FranchiseDTO.RegionAvgSalesDTO>> entry : dto.getFranchiseAvgSales().entrySet()) {
String year = entry.getKey();
Map<String, FranchiseDTO.RegionAvgSalesDTO> regionMap = entry.getValue();
if (regionMap != null) {
for (Map.Entry<String, FranchiseDTO.RegionAvgSalesDTO> regionEntry : regionMap.entrySet()) {
String region = regionEntry.getKey();
FranchiseDTO.RegionAvgSalesDTO regionDto = regionEntry.getValue();
if (regionDto != null) {
FranchiseAvgSales franchiseAvgSales = mapToFranchiseAvgSales(franchisor, year, region, regionDto);
franchiseAvgSalesRepository.save(franchiseAvgSales);
log.info("가맹점 평균매출 저장 완료: 연도={}, 지역={}", year, region);
}
}
}
}
//---------------------------------------------------------
// 가맹점 평균매출 저장 (batch)
if (dto.getFranchiseAvgSales() != null) {
List<FranchiseAvgSales> avgSalesBuffer = new ArrayList<>();
for (Map.Entry<String, Map<String, FranchiseDTO.RegionAvgSalesDTO>> entry : dto.getFranchiseAvgSales()
.entrySet()) {
String year = entry.getKey();
Map<String, FranchiseDTO.RegionAvgSalesDTO> regionMap = entry.getValue();
if (regionMap != null) {
for (Map.Entry<String, FranchiseDTO.RegionAvgSalesDTO> regionEntry : regionMap.entrySet()) {
String region = regionEntry.getKey();
FranchiseDTO.RegionAvgSalesDTO regionDto = regionEntry.getValue();
if (regionDto != null) {
avgSalesBuffer.add(mapToFranchiseAvgSales(franchisor, year, region, regionDto));
}
}
}
}
if (!avgSalesBuffer.isEmpty()) {
franchiseAvgSalesRepository.saveAll(avgSalesBuffer);
}
for (int index = 0; index < franchiseDataList.size(); index++) {
FranchiseDTO dto = franchiseDataList.get(index);
String regForLog = dto != null ? dto.getRegistrationNumber() : "null";
if (regForLog == null || existingRegs.contains(regForLog)) {
// 이미 존재하는 등록번호는 스킵
continue;
}
long itemStartNs = System.nanoTime();
try {
processFranchiseData(dto);
successCount++;
} catch (Exception e) {
log.error("데이터 처리 실패: 등록번호={}, 오류={}", regForLog, e.getMessage(), e);
failureCount++;
} finally {
long itemElapsedMs = (System.nanoTime() - itemStartNs) / 1_000_000;
log.info("({}/{}) 등록번호={} 처리 시간={} ms", index + 1, totalCount, regForLog, itemElapsedMs);
}
if ((index + 1) % FLUSH_CHUNK_SIZE == 0) {
entityManager.flush();
entityManager.clear();
log.debug("PersistenceContext flush/clear 수행 (index={})", index + 1);
}
}
영속성 컨텍스트안에 너무 많은 엔티티들이 쌓이지 않도록 주기적으로 flush 및 clear도 진행해주었습니다.
batch insert 적용 후 507개의 데이터를 넣는데 약 51.6초로 36분40초에서 51.6초로 약42배 빨라졌습니다.
pk 값으로 identify로 자동생성하는 키가 아닌, 등록번호로 설정했기 때문에 jpa설정만으로 batch insert를 사용할 수 있었습니다.
'개발 > 서버' 카테고리의 다른 글
| 프랜차이즈 데이터 관리 구조 개선: 33개 테이블에서 12개의 테이블로 (0) | 2025.08.20 |
|---|---|
| 프차천국 - 데이터 삽입 로직 비동기 처리 (1) | 2025.08.20 |
| '프차천국' 리빌딩: 커서(AI)의 개발적 한계 (4) | 2025.08.18 |
| Thymeleaf에서 권한별 사이드바 조건이 작동하지 않는 이유와 해결법 (0) | 2025.07.16 |
| 자잘한 오류의 세계 : Docker에서 Python 크롤러 + Spring Boot 통합 시 발생한 GLIBC 버전 오류 해결 (1) | 2025.07.15 |
댓글