본문 바로가기
개발/서버

프차천국 - 데이터 삽입 로직 최적화

by agong이 2025. 8. 20.

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를 사용할 수 있었습니다.

 

 

댓글