Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE_5] Spring Batch를 사용한 대용량 AI 메타 이미지 관리 #80

Open
3 tasks
pjhcsols opened this issue Feb 17, 2025 · 0 comments
Open
3 tasks
Assignees
Labels
feature 새로운 기능 추가

Comments

@pjhcsols
Copy link
Owner

pjhcsols commented Feb 17, 2025

어떤 기능인가요?

추가하려는 기능에 대해 간결하게 설명해주세요

작업 상세 내용

  • TODO
  • TODO
  • TODO

참고할만한 자료(선택)

Spring Batch를 활용한 성능 향상 및 최적화 방안

현재 구현된 @Scheduled 기반의 스케줄러 방식은 실행 시간이 길어질 경우 성능 저하와 트랜잭션 부하가 발생할 가능성이 큽니다. 이를 Spring Batch를 활용하여 개선하면 다음과 같은 성능 향상을 기대할 수 있습니다.


🛠 Spring Batch 최적화 전략

  1. Chunk 기반 처리

    • 기존에는 List를 한 번에 가져와서 필터링 후 삭제했지만, Spring Batch의 Chunk 기반 처리를 활용하면 메모리 효율성을 높일 수 있음.
    • 한 번에 대량 데이터를 처리하는 것이 아니라 일정 단위(Chunk)로 나눠서 처리 → 메모리 부족 문제 해결.
  2. 멀티스레드 및 병렬 처리

    • 기존에는 단일 스레드로 실행되므로, 실행 시간이 길어질 가능성이 있음.
    • Spring Batch의 멀티스레드 혹은 병렬 처리(Job Parallelization) 를 적용하여 속도를 개선.
  3. Step 기반 분리 (이미지 검증 → 삭제 → 업데이트)

    • 기존 로직은 하나의 메서드에서 모든 처리를 수행.
    • Spring Batch에서는 Step을 분리하여 유지보수성을 높이고, 각 Step의 성능을 최적화할 수 있음.

✅ 개선된 Spring Batch 로직

  1. Step 1: DB와 S3 (또는 로컬) 파일 리스트 비교

    • 데이터베이스에서 이미지 URL 조회
    • S3 또는 로컬 디렉토리에서 이미지 파일 목록 조회
    • 불필요한 데이터를 Chunk 단위로 로드하여 메모리 절약
  2. Step 2: 차집합 연산을 이용한 정리

    • DB에 존재하지 않는 S3 이미지를 배치 단위(Chunk)로 삭제
    • DB에는 있지만 디렉토리에 없는 이미지는 NULL로 업데이트
  3. Step 3: 비동기 병렬 처리로 이미지 삭제

    • 대량의 삭제 작업을 병렬 스레드(Pool) 를 활용하여 처리

🔧 코드 수정 (Spring Batch 적용)

아래는 기존 @Scheduled 방식 대신 Spring Batch를 활용한 최적화된 코드입니다.

1️⃣ Batch Job Configuration (Job 설정)

@Configuration
@RequiredArgsConstructor
public class ImageCleanupBatchConfig {
    
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;
    private final ImageItemReader imageItemReader;
    private final ImageProcessor imageProcessor;
    private final ImageWriter imageWriter;

    @Bean
    public Job imageCleanupJob() {
        return jobBuilderFactory.get("imageCleanupJob")
                .incrementer(new RunIdIncrementer()) 
                .start(fetchImageUrlsStep())
                .next(deleteUnusedImagesStep())
                .next(updateDbStep())
                .build();
    }

    @Bean
    public Step fetchImageUrlsStep() {
        return stepBuilderFactory.get("fetchImageUrlsStep")
                .<String, String>chunk(100)
                .reader(imageItemReader)
                .processor(imageProcessor)
                .writer(imageWriter)
                .build();
    }

    @Bean
    public Step deleteUnusedImagesStep() {
        return stepBuilderFactory.get("deleteUnusedImagesStep")
                .tasklet(new ImageDeletionTasklet())
                .build();
    }

    @Bean
    public Step updateDbStep() {
        return stepBuilderFactory.get("updateDbStep")
                .tasklet(new UpdateDatabaseTasklet())
                .build();
    }
}

2️⃣ ItemReader (DB 및 S3 목록 조회)

@Component
public class ImageItemReader implements ItemReader<String> {

    private final ProductService productService;
    private final S3StorageService s3StorageService;
    private Iterator<String> iterator;

    @Autowired
    public ImageItemReader(ProductService productService, S3StorageService s3StorageService) {
        this.productService = productService;
        this.s3StorageService = s3StorageService;
    }

    @Override
    public String read() {
        if (iterator == null) {
            List<String> s3Urls = s3StorageService.getAllImageUrls();
            iterator = s3Urls.iterator();
        }
        return iterator.hasNext() ? iterator.next() : null;
    }
}

➡️ 역할:

  • S3에서 모든 이미지 목록을 가져옴.
  • Chunk 단위로 데이터를 로드하여 메모리 사용 최소화.

3️⃣ Processor (불필요한 이미지 판별)

@Component
public class ImageProcessor implements ItemProcessor<String, String> {
    
    private final ProductService productService;

    @Autowired
    public ImageProcessor(ProductService productService) {
        this.productService = productService;
    }

    @Override
    public String process(String imageUrl) {
        List<String> databaseUrls = productService.getAllProductImageUrls();
        return databaseUrls.contains(imageUrl) ? null : imageUrl;
    }
}

➡️ 역할:

  • DB와 비교하여 불필요한 이미지 URL 필터링
  • null 반환 시 해당 데이터는 삭제 대상에서 제외됨

4️⃣ ItemWriter (S3에서 불필요한 이미지 삭제)

@Component
public class ImageWriter implements ItemWriter<String> {

    private final S3StorageService s3StorageService;

    @Autowired
    public ImageWriter(S3StorageService s3StorageService) {
        this.s3StorageService = s3StorageService;
    }

    @Override
    public void write(List<? extends String> urlsToDelete) {
        s3StorageService.deleteProductPhotos(urlsToDelete);
    }
}

➡️ 역할:

  • 필터링된 불필요한 이미지 URL을 S3에서 삭제

5️⃣ Tasklet (DB 업데이트 및 정리)

@Component
public class UpdateDatabaseTasklet implements Tasklet {

    private final EntityManager entityManager;

    @Autowired
    public UpdateDatabaseTasklet(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
        List<User> users = entityManager.createQuery("SELECT u FROM User u WHERE u.userImageUrl IS NOT NULL", User.class).getResultList();
        for (User user : users) {
            user.setUserImageUrl(null);
            entityManager.persist(user);
        }
        return RepeatStatus.FINISHED;
    }
}

➡️ 역할:

  • DB에 존재하지만 실제 파일이 없는 이미지 URL을 NULL로 업데이트.

🚀 최적화 효과

1. 성능 향상

  • Chunk 기반 처리: 한 번에 많은 데이터를 처리하는 대신 일정 단위로 처리하여 메모리 부담을 줄임.
  • 병렬 처리: 멀티스레드 기반으로 실행하여 이미지 삭제 속도를 개선.

2. 안정성 증가

  • @Transactional을 최소한으로 사용하여 데이터 일관성을 유지.
  • Spring Batch가 실패한 Step을 재시도(Retry) 할 수 있도록 구성.

3. 유지보수성 향상

  • Step을 분리하여 각각의 역할을 명확히 함.
  • Spring Batch의 Job/Step 구조로 변경하여 확장성을 고려.

🚀 결론

기존 @Scheduled 방식은 단순하면서도 직관적이지만, 대량 데이터를 처리할 때 메모리 과부하 및 속도 저하가 발생할 수 있음.
Spring Batch를 활용하면 Chunk 기반 처리, 병렬 실행, Step 분리 등을 통해 성능을 최적화하면서도 유지보수성을 높일 수 있음.

이제, 대량 이미지 삭제 작업이 서버 부하 없이 안정적으로 수행될 수 있음! 🚀🔥


🚀 Spring Batch를 사용한 대용량 AI 메타 이미지 관리 분석

Spring Batch는 대량의 데이터(이미지 메타데이터 포함)를 효율적이고 안정적으로 처리하는 데 최적화되어 있습니다. 현재 사용 중인 스케줄러 기반 이미지 관리 로직은 Spring Batch를 적용하면 성능과 유지보수성이 크게 향상될 수 있습니다.


🟡 Spring Batch를 적용하면 유리한 시나리오

1️⃣ 대규모 AI 생성 이미지 메타데이터 관리

  • AI가 생성한 수백만 건의 메타 이미지 파일 및 URLChunk 기반으로 관리.
    Spring Batch대량의 이미지 메타데이터병렬 처리해 속도를 높일 수 있습니다.

2️⃣ 사용되지 않는 이미지 및 메타데이터 정리

  • DB와 S3/로컬 디스크 간 불일치 이미지정기적으로 스캔 및 삭제.
    Spring Batch Step으로 **읽기(Read) → 처리(Process) → 쓰기(Write)**를 멀티 스레드로 진행해 빠른 속도로 처리.

3️⃣ AI 메타 이미지 재고 최적화 (스케일 아웃 처리)

  • 대량의 프로필 이미지, 상품 이미지, AI 가상 착용 이미지한 번에 일괄 처리.
    Spring Batch JobMulti-Step, Multi-Thread 방식으로 설계해 성능 최적화.

🟠 Spring Batch 적용 시 아키텍처 예시:

📊 1. AI 메타 이미지 관리 Spring Batch 아키텍처

[Batch Job: AIMetaImageCleanupJob]
  ├─ Step 1: Read (DB 및 S3/로컬 이미지 목록 읽기 - Reader)
  ├─ Step 2: Process (DB-S3 차집합 비교, 불일치 이미지 식별 - Processor)
  └─ Step 3: Write (S3/로컬 파일 삭제 및 DB 업데이트 - Writer)

🛒 2. 프로필 및 상품 이미지 스케줄링 아키텍처

[Spring Batch Job: UserProfileAndProductImageJob]
  ├─ Step 1: Read (DB의 모든 사용자 프로필 및 상품 이미지 목록 조회)
  ├─ Step 2: Process (AI 메타데이터 필터링 및 교차검증)
  └─ Step 3: Write (DB 업데이트 및 불필요 이미지 S3 삭제)

🟢 Spring Batch 적용 시 장점:

장점 설명
대량 처리 최적화 청크(Chunk) 기반으로 수백만 건의 이미지 메타데이터를 빠르게 처리
병렬 및 멀티스레드 처리 Step 및 Partitioning을 통한 고속 병렬 처리
재시작 및 실패 복구 (Checkpoint & Restart) 중단 시 중간 지점부터 재개
정합성 보장 (ACID 트랜잭션) DB 업데이트를 트랜잭션으로 관리
스케줄링과 통합 Spring Scheduler 및 Cron 표현식과 통합 가능
모듈화 및 재사용성 Reader, Processor, Writer를 모듈화해 재사용성 향상

🔴 Spring Batch가 불필요한 경우:

상황 이유
실시간 AI 이미지 처리 실시간성 요구 시 Kafka + Redis가 더 적합
소량의 데이터 업데이트 단일 사용자 업데이트라면 Batch는 과잉설계

🚀 Spring Batch와 S3, 로컬스토리지 통합 Best Practice:

작업 유형 추천 기술 스택
AI 메타 이미지 일괄 정리 Spring Batch (Multi-Step, Chunk-Oriented)
S3 이미지 불일치 정리 Spring Batch + AWS S3 SDK
사용자 프로필 이미지 정리 Spring Batch + JPA (Reader/Writer)
상품 이미지 삭제 및 S3 동기화 Spring Batch + S3 SDK + Lambda

📝 결론:

Spring Batch는 AI 메타 이미지와 같은 대규모 데이터 정리에 최적입니다.
특히 "읽기 → 처리 → 쓰기" 패턴이 반복되는 정기 작업에 강점을 가집니다.
현재의 단순 스케줄러 기반 로직Spring Batch Job으로 전환하면,
대량 처리 성능, 재시작 안전성, 트랜잭션 관리까지 모두 확보할 수 있습니다. 🚀✨

@pjhcsols pjhcsols added the feature 새로운 기능 추가 label Feb 17, 2025
@pjhcsols pjhcsols self-assigned this Feb 17, 2025
@pjhcsols pjhcsols moved this to Ready in BASILIUM_I_award Feb 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature 새로운 기능 추가
Projects
Status: Ready
Development

No branches or pull requests

1 participant