CS 정리

Java 대용량 데이터 처리(1) - Cursor 방식

문쿼리 2025. 4. 20. 13:04

1. 한 번에 처리하기 -> 작게 나눠서 천천히 처리하기

Cursor-based 예제

Bad case : 모두 조회, 모두 처리

List<Data> allData = repository.findAll();
processAll(allData);

Good case : Cursor 방식으로, 배치 사이즈로 조회하여, 부분처리

Long lastId = 0L;
int batchSize = 1000;

while (true) {
    List<Data> batch = repository.findBatch(lastId, batchSize);
    if (batch.isEmpty()) break;

    processAll(batch);

    lastId = batch.get(batch.size() - 1).getId();
}

@Query(value = "SELECT * FROM data_table WHERE id > :lastId ORDER BY id ASC LIMIT :limit", nativeQuery = true)
List<Data> findBatch(@Param("lastId") Long lastId, @Param("limit") int limit);

시간순서의 처리가 필요한 경우 (createdAt + id 복합 커서)

LocalDateTime lastCreatedAt = LocalDateTime.of(2023, 1, 1, 0, 0);
Long lastId = 0L;
int batchSize = 1000;

while (true) {
    List<Data> batch = repository.findNextBatch(lastCreatedAt, lastId, PageRequest.of(0, batchSize));
    if (batch.isEmpty()) break;

    processAll(batch);
    
    Data last = batch.get(batch.size() - 1);
    lastCreatedAt = last.getCreatedAt();
    lastId = last.getId();
}

@Query("""
    SELECT d FROM Data d 
    WHERE (d.createdAt > :lastCreatedAt) 
       OR (d.createdAt = :lastCreatedAt AND d.id > :lastId)
    ORDER BY d.createdAt ASC, d.id ASC
""")
List<Data> findNextBatch(@Param("lastCreatedAt") LocalDateTime lastCreatedAt,
                         @Param("lastId") Long lastId,
                         Pageable pageable);

 

 

요약 정리

1. 대용량 처리 시 id 커서 방식은 간단하고 빠름

2. createAt + id 커서는 정렬 기준이 중요할 때 안정적