일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- mysql
- java
- 테슬라
- JPA
- Docker
- Query
- KNN
- api cache
- aggs
- IONQ
- NORI
- dbeaver
- Elastic
- Elasticsearch
- java crawler
- Analyzer
- Cache
- 아이온큐
- request cache
- 양자컴퓨터
- vavr
- file download
- Aggregation
- API
- Selenium
- aqqle
- elasticsearch cache
- ann
- TSLA
- redis
- Today
- Total
아빠는 개발자
[java] 멀티스레드 최적화 (java application batch) - 이론편 본문
멀쩡하던 배치가 느려졌다.
것도.. 한 5-6배? ㅋㅋㅋㅋ 어처구니가 없는 .. 색인 쿼리중 참조 하는 테이블의 데이터가 변해서 맛이 간거 같은게.. 예상이지만..
쿼리를 이것저것 돌려봐도 그렇게 느리진 않았는데..
뭐 암튼..
성능개선의 하나의 방법으로 병렬처리 성능을 올려보자..
현재 우리회사의 배치는
8core 에서 2개의 스레드로 실행되고 전체,증분 색인 두개가 동시에 돈다면 2개의 스레드를 사용하는 어플리케이션이 2개가 실행된다.
그럼 max 를 4까지 올려봐도...
1. 시스템 자원 파악
먼저, 서버나 시스템의 전체 CPU 코어 수, 메모리와 같은 자원을 파악해야 합니다. 각 애플리케이션이 실행되는 환경에서 사용 가능한 자원의 양을 기준으로 적절한 스레드 수를 할당해야 합니다.
int availableCores = Runtime.getRuntime().availableProcessors();
2. 각 애플리케이션별 스레드 수 할당
모든 애플리케이션이 동시에 실행될 경우, 시스템의 전체 자원에 비례하여 각 애플리케이션에 적절한 스레드 수를 할당하는 것이 중요합니다. 예를 들어, 8개의 CPU 코어를 가진 시스템에서 4개의 애플리케이션이 실행된다면, 각 애플리케이션에 2개의 스레드를 할당하는 것이 기본적인 전략이 될 수 있습니다.
하지만, 각 애플리케이션의 성격에 따라 다르게 할당할 수 있습니다. 예를 들어:
- CPU 바운드(CPU Bound) 작업이 많은 애플리케이션은 CPU 코어 수에 맞춰 스레드 수를 제한해야 합니다.
- IO 바운드(IO Bound) 작업이 많은 애플리케이션은 CPU 코어보다 약간 더 많은 스레드를 사용할 수 있습니다.
// 각 애플리케이션별 CPU 코어 나누기
int coresPerApp = availableCores / numberOfApplications;
// 각 애플리케이션에 최적의 스레드풀 크기 설정
ExecutorService executor = Executors.newFixedThreadPool(coresPerApp);
3. 스레드풀에 대한 공통 고려사항
- 스레드 수 제한: 애플리케이션이 무작정 많은 스레드를 생성하지 않도록, 고정된 스레드풀을 사용하여 자원 낭비를 방지합니다.
- Thread Factory 사용: 스레드 풀의 동작을 제어하거나 모니터링하기 위해 ThreadFactory를 사용해 커스텀 스레드를 만들 수 있습니다. 스레드 네이밍이나 우선순위를 조정할 수 있습니다.
ExecutorService executor = Executors.newFixedThreadPool(coresPerApp, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("CustomThread-" + thread.getId());
return thread;
}
});
ThreadPoolTaskExecutor는 스프링 프레임워크에서 자주 사용하는 스레드 풀로, 비동기 작업을 처리하는 데 자주 활용됩니다. ThreadPoolTaskExecutor는 ExecutorService와 비슷한 역할을 하지만, 스프링 환경에 맞게 좀 더 많은 설정을 제공합니다.
주요 설정으로는 **최대 스레드 수 (max thread)**와 **코어 스레드 수 (core thread count)**를 관리할 수 있으며, 이 두 가지 설정을 적절히 구성하여 최적화된 스레드풀을 구성할 수 있습니다.
주요 설정 항목
- Core Pool Size (코어 스레드 수):
- 스레드풀에서 기본적으로 유지되는 최소 스레드 수를 정의합니다.
- 요청이 발생할 때, 기본적으로 이 수만큼의 스레드가 항상 생성되어 대기합니다.
- 요청이 많아지면 추가로 스레드가 생성될 수 있지만, 생성된 스레드가 core pool size 이하라면 이 스레드들은 작업이 끝나도 사라지지 않고 계속 유지됩니다.
executor.setCorePoolSize(5); - Max Pool Size (최대 스레드 수):
- 스레드풀이 생성할 수 있는 최대 스레드 수를 정의합니다.
- 작업 요청이 코어 스레드 수를 초과하면, 스레드풀은 max pool size까지 추가 스레드를 생성합니다.
- 그러나 이 값을 초과하면 더 이상 스레드를 생성하지 않고, 작업이 큐에 쌓이거나 거부 정책에 따라 처리됩니다.
executor.setMaxPoolSize(10); - Queue Capacity (큐 용량):
- 코어 스레드 수만큼 스레드가 사용 중일 때, 추가 작업을 처리하기 위해 대기할 수 있는 큐의 크기를 정의합니다.
- 큐 용량이 다 차면, 새로운 작업은 추가로 생성된 스레드에 의해 처리되며, 스레드가 최대치에 도달하면 작업을 거부할 수 있습니다.
executor.setQueueCapacity(25); - Keep Alive Time (스레드 유지 시간):
- max pool size까지 확장된 스레드가 일정 시간 동안 아무 작업도 하지 않으면, 그 스레드를 종료시키는 시간을 설정합니다.
- 기본적으로 코어 스레드들은 이 설정과 상관없이 계속 유지되지만, 코어 스레드를 초과하는 스레드는 이 시간이 지나면 제거됩니다.
executor.setKeepAliveSeconds(60); - RejectedExecutionHandler (거부 정책):
- 큐가 가득 차고 max pool size에도 도달했을 때, 새로운 작업을 어떻게 처리할지 결정하는 정책입니다.
- 기본적으로 AbortPolicy가 설정되어 있으며, 이는 예외를 던집니다. 다른 옵션으로는 CallerRunsPolicy (요청한 쓰레드에서 실행), DiscardPolicy (무시), DiscardOldestPolicy (가장 오래된 작업을 삭제)가 있습니다.
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
작동 방식
- 코어 스레드 수 이하의 요청: 작업 요청이 코어 스레드 수(예: 5) 이하이면, 작업은 즉시 처리됩니다.
- 코어 스레드 수 초과, 큐 여유 있음: 작업 요청이 코어 스레드 수를 초과하더라도 큐 용량(예: 25)이 남아 있다면, 요청은 큐에 대기합니다.
- 코어 스레드 초과, 큐 꽉 참: 큐가 가득 차면, 스레드풀은 최대 스레드 수(예: 10)까지 추가 스레드를 생성하여 작업을 처리합니다.
- 최대 스레드 초과: 최대 스레드 수와 큐가 가득 찬 경우에는 RejectedExecutionHandler에 따라 거부 정책을 적용합니다.
최적화 방법
- 코어 스레드 수: CPU 바운드 작업의 경우, CPU 코어 수에 맞게 설정하는 것이 좋습니다. IO 바운드 작업이 많은 경우에는 더 많은 스레드를 허용할 수 있습니다.
- 큐 용량: 큐 용량을 너무 작게 설정하면 스레드풀의 최대 스레드 수에 빨리 도달할 수 있으며, 너무 크게 설정하면 응답성이 저하될 수 있습니다.
- 최대 스레드 수: 시스템 자원(CPU, 메모리)을 고려하여 적절히 설정합니다. 너무 많은 스레드를 허용하면 성능 저하가 발생할 수 있습니다.
이러한 설정을 통해 애플리케이션의 비동기 작업을 효율적으로 처리할 수 있으며, 스레드풀의 구성은 작업의 특성과 시스템의 리소스 상황에 맞게 적절히 튜닝해야 합니다.
Yahoo 에서 협찬 받은 데이터 38,773개를 조회해서 파일로 쓰는 로직으로 테스트 해보자
'Java' 카테고리의 다른 글
[java] 멀티스레드 최적화 (java application batch) - 테스트 편 (0) | 2024.09.22 |
---|---|
[java] 함수형 프로그래밍(Functional Programming) 은? (0) | 2024.09.02 |
[java] Vavr 란? (0) | 2024.09.02 |
[java] package 에서 common 과 base 의 의미 (0) | 2024.06.11 |
[java] URL 호출해서 파일 다운로드 (InputStream) (0) | 2024.06.01 |