본문 바로가기
BackEnd/이슈 정리

[Webflux] 대용량 데이터 처리 heap 사용량 이슈

by 하용권 2024. 8. 3.

회사에서 일을 하다가 마주친 이슈입니다.

 

 

1. 문제 상황

데이터를 외부에서 수집해서 서비스에 맞게 파싱 후, 저희 db와 redis에 넣는 작업을 했었습니다.

데이터 자체는 크지 않았지만, 서비스에 맞게 사용하기 위해 가공한 후의 데이터가 많았습니다.

 

여기서 문제는 가공하고 mongoDB와 redis에 넣을 때 힙을 약 5기가 정도로 많이 사용하고 있었습니다.

 

그래서 왜 이렇게 많이 힙을 사용하는 지 알아봤습니다.

 

 

2. 원인 파악

flatMap을 이용해서 데이터를 처리하고 있었습니다.

 

void test() {
        
    List<String> data = new ArrayList<>();

    Flux.just(data)
            .flatMap(fetchData -> {
                //db에 저장
                return Mono.just(fetchData);
            })
            .flatMap(fetchData -> {
                //redis 에 저장
                return Mono.just(fetchData);
            });
        
}

(실제로 회사에서 이러한 코드를 사용하는 건 아닙니다.

 

실제 데이터를 가져오고, 이를 가공한 후에 넣는데 문제는 flatMap을 사용하다보니 이 과정이 비동기로 동작했습니다.

예를 들면, 저희 api1, api2, api3에 사용하기 위한 데이터를 가공을 하게 되는데 이 데이터를 전부 비동기로 db에 넣고 있었습니다.

그러다보니 힙을 예상보다 많이 사용하게 되었습니다.

 

또한 가공을 할 때, 모든 데이터를 한 번에 가공했었습니다.

데이터가 200만 개라고 가정하면, 200만 개를 전부 한 번에 가공하고 데이터를 db에 넣는 등 작업을 했었습니다.

가공을 하게 되면서 데이터의 사이즈가 늘어나게 되고 이 과정도 문제가 되었습니다.

 

 

3. 해결 방안

일단 모든 데이터를 한 번에 처리하던 것을 일정 개수로 나눠서 가공하고 db 등에 넣도록 수정했습니다.

하지만 이것만 했을 때는 큰 효과가 없었습니다.

왜냐하면 비동기로 동작하기 때문에 나누더라도 모든 데이터가 한번에 가공이 되기 때문입니다.

 

그래서 비동기로 동작하던 것을 동기로 변경을 시켜주고 싶었습니다.

그러기 위해서 flatMap의 concurrency를 제어해주었습니다.

 

flatMap(.... , {concurrency}) 를 통해 설정할 수 있습니다.

이를 1로하니 동기처럼 동작하는 것을 확인했습니다.

(로그 통해서)

 

 

실제로 힙이 5기가 사용하던 것이, 3기가로 극적으로 줄었습니다.

 

하지만 성능은 30초 걸리던 것이 40초로 늘어났습니다.

이러한 트레이드 오프를 찾는 것이 중요한 것 같습니다.

 

 

4. 느낀 점

기존에 스케줄러로 데이터를 가져와서 처리하고 있어서 그대로 썼었습니다.

spring batch가 대용량 데이터 처리에 좋다고 하는데, 이를 이용한다면 이러한 작업을 수월하게 할 수 있지 않을까 라는 생각이 들었습니다.

 

spring batch는 아직 한 번도 안 써봐서 이것도 공부를 해봐야 할 것 같습니다.

(그래서 현재 조그만한 회사 프로젝트를 코틀린 + batch로 작업하고 있습니다.)

반응형