[Java/SpringBoot/Redis] 집계 성능 개선하기

Java17 / SpringBoot 3.1.2 / Redis

비동기 처리와 Redis 캐싱을 통한 성능 개선을 해봤다. 최근 프로젝트에서 발생한 TimeOut 문제를 해결하고 데이터 처리 성능을 향상하기 위해 비동기 처리와 Redis 캐싱을 도입하였다. 주요 개선 사항은 다음과 같다.

문제 원인

기존의 데이터 집계 프로세스에서는 대규모 요청을 처리하는 동안 TimeOut이 빈번하게 발생하였다. 이로 인해 다음 두 가지의 개선이 필요하였다:

  1. 비동기 처리: 시간 소모가 큰 집계 작업을 비동기적으로 처리하여 API 응답 시간을 단축할 필요가 있었다.
  2. 중복 집계 방지: 동일한 파라미터로 중복 요청이 발생할 경우 데이터 무결성 문제를 방지하기 위해 Redis 캐싱을 도입하였다. 또한 한번 집계하는데 상당히 많은 DB I/O가 일어나기에 성능적 이점도 고려했다.

적용된 개선 사항

  • 중복 집계 방지 로직 추가: 수동 집계 요청 시 중복 집계가 발생하지 않도록 Redis 캐시를 사용하여 실행 상태를 관리하였다.
  • 집계 수행 메서드는 Redis만 바라보도록 분리하였다.
  • @Async로 TimeOut 방지를 했다.

처리 흐름

구체적으로 적용한 개선 사항을 단계별로 설명한다.

  1. 실행 상태 캐시 관리

작업 상태를 캐싱하여 중복 집계를 방지하였다. 작업의 실행 여부를 관리하는 캐시로, 집계 요청에 대한 작업 상태를 관리하며, 해당하는 작업이 실행 중인지 확인할 수 있다.

캐시 이름을 CACHE_KEY_EXECUTION_STATE 로 두고, 요청 파라미터를 키로 만들었다.

  1. 커맨드 큐 캐시

개별 일자별 작업 상태를 관리하는 캐시로 요청 기간의 단일 일자별 작업을 고유 키로 관리하여 각 날짜에 대해 독립적으로 관리하는 용도로 사용했다.

캐시명은 CACHE_KEY_COMMAND_QUEUE, 날짜와 기타 집계에 필요한 값을 키로 만들었다.

  1. 비동기 처리 비동기를 적용해 백그라운드에서 대규모 집계 작업을 처리하고, Timeout을 해결했다.

이 메서드를 실행하면 커맨드 큐 캐시에서 꺼내온 키들로 구성된 작업 큐를 순회하며 개별 커맨드를 비동기로 처리한 후, 완료된 커맨드를 캐시에서 삭제한다.

@Async
public void executeCommands(CacheStateDto cacheStateDto) {
    try {
        Queue<String> commandQueue = new LinkedList<>();
        
        // Redis에서 커맨드 키들을 로드하여 큐에 추가
        Set<String> keys = redisTemplate.keys("commandQueue*");
        if (keys != null && !keys.isEmpty()) {
            commandQueue.addAll(keys);
        }

        // 커맨드 큐를 순회하며 각 커맨드 실행
        while (!commandQueue.isEmpty()) {
            String key = commandQueue.poll();
            Command command = (Command) redisTemplate.opsForValue().get(key);

            if (command == null) {
                continue;
            }

            processCommand(command);  // 각 커맨드에 대한 개별 집계 처리
            redisTemplate.delete(key);  // 처리 완료된 커맨드 캐시 삭제
        }
    } catch (Exception e) {
        log.error("집계 처리 중 오류 발생", e);
    } finally {
        // 모든 작업 완료 후 실행 상태 캐시 초기화
        updateCacheState(cacheStateDto, false);
    }
}

추후 해당 집계 배치를 붙이기 위해 해당메서드가 Redis만 바라보도록 작성하였다. 백엔드에서는 집계 대상을 키로 만들어 Redis에 쌓기만 하고, 이 메서드에서는 Redis 키를 가져와 집계하는 방식으로 분리했다.

  1. 예외 처리 작업 도중 예외가 발생하면, 모든 관련 캐시를 삭제하여 일관성을 유지했다. 비동기 처리중 발생하는 예외는 서버 로그에 기록하여 추적할 수 있도록 하였다.

결론

중복집계 방지, 비동기 처리를 통해 데이터 집계 성능을 크게 개선할 수 있었다. 라이브 올라가면 Dynatrace 통해 구체적인 성능 지표도 확인해 볼 예정이다.


© 2022. All rights reserved.

Powered by Hydejack v9.2.1