업무 기록/ETC

스레드 개수 설정 및 성능 최적화와 방법들

code2772 2025. 2. 20. 08:44
728x90
반응형

 

 

1. 스레드란?

 

스레드는 프로그램에서 실행되는 가장 작은 단위의 작업 흐름입니다. 여러 개의 스레드를 사용하면 병렬 처리를 통해 성능을 향상시킬 수 있습니다. CPU가 여러 개의 스레드를 동시에 실행할 수 있기 때문에, 적절한 개수의 스레드를 사용하면 응답 속도를 개선하고 자원을 효율적으로 활용할 수 있습니다.

 

 

스레드 개수 설정 방법 및 적정 개수 판단 방법

비동기 작업에서 스레드 풀(ThreadPool) 크기를 설정할 때,

  • *"최적의 스레드 개수"**를 결정하는 것은 매우 중요한데, 이를 판단하는 방법을 설명할게요.

 

1. 스레드 개수 설정 기준 (CPU vs. I/O 작업에 따라 다름)

스레드 개수를 설정할 때는 어떤 종류의 작업을 처리하는지에 따라 다르게 결정해야 함.

작업 유형 설명 스레드 개수 설정 방식

CPU 바운드 작업 연산이 많은 작업 (예: AI 연산, 암호화, 데이터 분석) CPU 코어 수 + 1~2
I/O 바운드 작업 네트워크 요청, DB 조회, 파일 처리 등 CPU 코어 수 × 2~10

 

스레드 개수를 결정하는 가장 기본적인 공식은 다음과 같습니다:

최적 스레드 개수 = (CPU 코어 수 * (1 + (대기 시간 / 처리 시간)))

 

예를 들어, CPU 코어가 4개이고, 대기 시간이 처리 시간의 50%라면,

4 * (1 + 0.5) = 6

이므로 최적 스레드 개수는 6개가 됩니다.

 

비동기 이미지 서버의 경우?

  • 네트워크 요청(이미지 다운로드), 파일 저장, DB 처리 등 I/O 바운드 작업이 많음 → 스레드 개수를 더 크게 설정하는 것이 유리함
  • 일반적으로 코어 수의 2~10배 사이에서 조정하는 것이 적절함.

 

2. 적절한 스레드 개수 결정하는 방법

 

최적의 스레드 개수를 찾기 위해 사용 가능한 방법들을 정리해볼게요.

 

1) 공식적인 계산법

 

최적 스레드 개수 = (응답 시간 × 요청 빈도) ÷ 병렬 작업 수

예제: 100ms 걸리는 API를 초당 1000건 처리해야 한다면?

최적 스레드 개수 = (0.1초 × 1000건) ÷ 병렬 작업 수

  • 병렬 작업이 10개라면 → 10개의 스레드 필요
  • 병렬 작업이 20개라면 → 5개의 스레드 필요

 

2) 직접 테스트 (Thread Dump & Monitoring Tools 활용)

 

스레드 개수를 조정하면서 실제로 얼마나 효율적으로 작동하는지 성능 테스트를 해볼 수도 있음.

 

# JVisualVM (Java VisualVM)

  • JVM 성능을 모니터링하고, 스레드 사용량 분석 가능

# JConsole

  • Spring Boot에서 실행되는 현재 스레드 개수, CPU 사용량을 실시간 확인 가능

# Spring Actuator + Micrometer + Prometheus + Grafana 조합

  • Spring Actuator와 Micrometer를 설정하면 현재 실행 중인 스레드 개수와 서버 부하 확인 가능

 

예제) Micrometer로 현재 스레드 개수 확인

java
복사편집
@Bean
public MeterBinder threadPoolMetrics(ThreadPoolTaskExecutor executor) {
    return (registry) -> registry.gauge("thread.pool.size", executor.getThreadPoolExecutor(), ThreadPoolExecutor::getPoolSize);
}

 

이렇게 설정하면 Prometheus로 현재 스레드 개수를 실시간으로 모니터링 가능!


 

3) 실전 환경에서 부하 테스트 (Load Testing Tools 활용)

 

# JMeter, Gatling, Locust 등을 활용해서 실제 트래픽 부하 테스트를 진행

# 초당 몇 개의 요청을 처리할 수 있는지 측정하면서 적정 스레드 수 결정

 

 JMeter 테스트 예제 및 참고 링크

  1. 1000개의 요청을 동시에 보냄
  2. 서버 응답 시간과 CPU 사용량 분석
  3. 스레드 개수를 변경하면서 최적의 값 찾기

https://hunseop2772.tistory.com/288

 

JMeter 부하테스트 전 설치

다양한 형태의 소프트웨어 및 하드웨어 리소스에 대한 성능 테스트와 부하 테스트를 수행하기 위해 사용되는 도구이며 JMeter는 아파치 소프트웨어 재단에서 개발되며, 다양한 프로토콜과 기술

hunseop2772.tistory.com

 

https://hunseop2772.tistory.com/289

 

JMeter 부하테스트 - 다중 스레드, 랜덤값, 선언 등

부하 테스트(Load Testing)는 컴퓨터 시스템, 네트워크, 소프트웨어 응용 프로그램 또는 웹 사이트의 성능과 안정성을 평가하는 소프트웨어 테스트의 한 형태입니다. 이 테스트는 특정 시스템이나

hunseop2772.tistory.com

 


 

3. 최적의 스레드 개수는 몇 개가 적당할까?

 

일반적인 추천값 (I/O 바운드 작업 기준)

  • 최소: CPU 코어 수 × 2
  • 최대: CPU 코어 수 × 10

 

예제) 8코어 CPU 기준

  • 최소 16개 (8 × 2)
  • 최대 80개 (8 × 10)

 

대용량 데이터 처리에서의 스레드 활용

 

1.  대용량 데이터 처리 개요

 

대용량 데이터를 효과적으로 처리하려면 병렬 처리를 활용하여 속도를 높이고 시스템 부하를 줄여야 합니다.

 

2.  스레드 풀(Thread Pool) 활용

 

대량의 데이터를 처리할 때 스레드를 효율적으로 관리하기 위해 **스레드 풀(Thread Pool)**을 사용합니다. Java에서는 Executors.newFixedThreadPool(n)을 이용하여 고정된 개수의 스레드를 실행할 수 있습니다.

 

ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
    int taskId = i;
    executorService.submit(() -> {
        System.out.println("Processing task: " + taskId);
    });
}
executorService.shutdown();

 

3.  비동기 처리 적용

 

Spring Boot에서는 @Async 어노테이션을 사용하여 비동기적으로 데이터를 처리할 수 있습니다.

@Service
public class DataProcessingService {
    @Async
    public CompletableFuture<String> processData(String data) {
        // 비동기 데이터 처리
        String result = processDataInternally(data);
        return CompletableFuture.completedFuture(result);
    }
    private String processDataInternally(String data) {
        // 실제 데이터 처리 로직
        return "Processed: " + data;
    }
}

 

4.  대량 데이터 분할 처리

 

하나의 스레드가 모든 데이터를 처리하면 성능이 저하될 수 있기 때문에, 데이터를 여러 개의 블록으로 나누어 병렬로 처리하는 전략이 필요합니다.

int totalDataSize = 10000;
int batchSize = 1000;
for (int i = 0; i < totalDataSize; i += batchSize) {
    List<Data> batch = fetchData(i, batchSize);
    executorService.submit(() -> processBatch(batch));
}

 

5. Kafka와 같은 메시지 큐 활용

 

Kafka와 같은 메시지 큐를 활용하여 데이터 처리량을 조절하고, 분산 시스템을 구성할 수 있습니다.

@KafkaListener(topics = "data_topic", groupId = "data_group")
public void listen(String message) {
    processMessage(message);
}

 

결론

 

대용량 데이터를 효과적으로 처리하려면 스레드 개수 최적화, 스레드 풀 활용, 비동기 처리, 데이터 분할 처리, 메시지 큐 활용과 같은 기법을 조합하는 것이 중요합니다. 이를 통해 시스템의 안정성과 성능을 모두 확보할 수 있습니다.

 

실전 적용 팁

 

1 : I/O 바운드 작업이면 코어 수의 2~10배 사이에서 설정

2 : 초기값을 CPU 코어 수 × 4 정도로 설정한 후 조정

3 : JVisualVM, Micrometer, JMeter 등을 활용해 부하 테스트 진행

4 : 스레드 개수를 점진적으로 조정하면서 최적화

5 : 서버 상태를 모니터링하면서 과부하 발생 여부 확인

 

 결론:

최적의 스레드 개수는 고정값이 아니라 테스트와 모니터링을 통해 조정하는 것이 가장 중요함!

대용량 데이터를 효과적으로 처리하려면 스레드 개수 최적화, 스레드 풀 활용, 비동기 처리, 데이터 분할 처리, 메시지 큐 활용과 같은 기법을 조합하는 것이 중요합니다. 이를 통해 시스템의 안정성과 성능을 모두 확보할 수 있습니다.

 

 

 

 

 

 

반응형