티스토리 뷰
📌 원래의 방식
여정들을 부를 때 마다 각각의 API를 호출하기 때문에 삽입 부분에서 많은 시간이 걸렸었다.
📝 기존 코드
List<Itinerary> saveItineraryList = new ArrayList<>();
for (ItineraryRequest ir : itineraryRequests) {
Itinerary saveItinerary = itineraryRepository.save(
ItineraryFactory.getItineraryEntity(trip, ir)
);
saveItineraryList.add(saveItinerary);
}
매 여정들 마다 save를 불렀었다.
이 당시 걸린 시간은 약 2초 정도 걸렸었다. (여정 13개 기준)
여정들이 무수히 많아지면 시간이 더 오래 걸릴것 같았고, 성능을 개선해보기로 시작.
🌟 1차 수정 : saveAll로 바꾸기
📌 save 와 saveAll의 차이
save를 한번 호출할 때마다 하나의 트랜잭션이 처리된다.
saveAll를 하게 되면, List 전체에 하나의 트랜잭션이 처리가 돼서, 시간이 단축된다.
즉 대량의 데이터를 일괄 처리하게 되므로, List 저장시에는 훨씬 효율적이다.
📝 1차 변경 코드
List<Itinerary> itineraryList = new ArrayList<>();
for (ItineraryRequest ir : itineraryRequests) {
itineraryList.add(ItineraryFactory.getItineraryEntity(trip, ir));
}
List<Itinerary> saveItineraryList = itineraryRepository.saveAll(itineraryList);
걸린 시간 : 2초에서 1초로 감소
🌟 2차 수정 코드 : 비동기적으로 바꿔보자!
📌 비동기적 방식이란?
기존의 방식은 단일 스레드 환경이라, 하나의 api 호출을 끝내면 다음 api 호출을 하는 형식으로 진행했다.
다음과 같이 방식을 진행할 경우, api 호출이 많아지면, 그 모든 api 호출이 끝날 때까지 기다려줘야하기 때문에
요청 데이터가 많아질 수록, 시간은 더 오래 걸린다.
하지만, 비동기적 방식 (Asynchronous)를 하게 되면,
동시다발적으로 api를 부를 수 있으므로, 시간상 절약이 된다.
단 주의해야할 점이 있다
🧐 ArrayList?
ArrayList는 단일 스레드 환경에서 유리한 컬렉션이다.
그래서 멀티 스레드 환경에 들어가게 되면, 예상치 못한 결과를 만나게 될 수 도 있다.
그래서 멀티 스레드 환경에서는 ConcurrentLinkedQueue 같은 특화된 자료구조를 사용하는 것이 좋다.
📌 ConcurrentLinkedQueue?
동기화된 메소드를 제공하는 컬렉션. 여기서 Concurrent는 동시에 여러 작업 (병렬작업)을 뜻함.
하나의 메소드마다 동기화 처리를 해주어서, 스레드 환경에서도 안전하게 데이터를 보관해준다.
단, 동기화 처리를 해주기 때문에 성능상 오래 걸린다.
ConcurrentLinkedQueue는 Queue를 멀티 스레드 환경에서 사용하기 좋게 만든 것이다.
Queue 자체가 선입선출 자료구조 이므로, ArrayList와 비슷하다고 생각해서 이번에 사용하였다.
📝 2차 변경 코드
- ExecutorService 선언
자바에서 제공하는 스레드 풀을 생성해주는 도구
스레드를 생성, 종료시킬 수 있다.
private static final ExecutorService executorService = Executors.newFixedThreadPool(10);
// 최대 10개의 스레드를 사용하는 ExecutorService 생성 (조정 가능)
- CompletableFuture 사용
자바에서 병렬 작업을 할 수 있도록 해주는 도구. supplyAsync(), runAsync() 를 통해 실행할 수 있다.
- ExecutorService와 CompletableFuture 같이 사용하는 이유
CompletableFuture도 자체적으로 기본 스레드 풀을 제공하지만, ExecutorService를 사용하면 사용자가 커스텀한 스레드풀을 사용할 수 있다. 그래서 스레드의 생성, 종료의 작업에서 제한을 둘 수 있어, 같이 사용했다.
ConcurrentLinkedQueue<Itinerary> itineraryList = new ConcurrentLinkedQueue<>();
List<CompletableFuture<Void>> apiCalls = new ArrayList<>();
for (ItineraryRequest ir : itineraryRequests) {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
itineraryList.add(ItineraryFactory.getItineraryEntity(trip, ir));
}, executorService);
apiCalls.add(future);
}
// 모든 API 호출이 완료될 때까지 대기
CompletableFuture<Void> allOf = CompletableFuture.allOf(apiCalls.toArray(new CompletableFuture[0]));
allOf.join(); // 모든 API 호출이 완료될 때까지 대기
executorService.shutdown(); // ExecutorService 종료
List<Itinerary> saveItineraryList = itineraryRepository.saveAll(itineraryList);
걸린 시간 : 1초 에서 0초대로 감소
🌟 최종 시간 비교
'Spring boot' 카테고리의 다른 글
[기능 개발] MySql에 QR code 이미지 저장하고, 결제 처리하기 기능 개발기 (QR code 결제) (1) | 2023.12.08 |
---|---|
[성능 개선] 상품 주문 시, 비관적 Lock으로 동시성 제어하기 : curl을 이용한 동시성 테스트 (0) | 2023.11.30 |
[리팩토링 & 예외처리] 인터페이스를 이용한 enum으로 예외처리 정돈하기. (0) | 2023.11.15 |
[예외처리] @Valid message를 response에 담아 출력하는 방법. (1) | 2023.11.13 |
[리팩토링] if 문 없이 객체 만들어보자 - 팩토리 패턴, 상속관계 엔티티 생성 (0) | 2023.10.28 |
- Total
- Today
- Yesterday
- 백엔드
- TiL
- boj
- 패스트캠퍼스
- 자료구조
- 백준
- 야놀자
- 스터디후기
- 패스트캠퍼스강의
- qjzl
- #국비지원취업
- 부트캠프
- 프로젝트후기
- 그룹스터디
- 국비지원캠프
- 채팅기능개발
- Java
- 백엔드부트캠프
- springboot
- 데이터베이스
- 국비지원
- 과정중간회고
- 자료구조 #스택 #큐 #덱 #선형자료구조
- 백엔드개발자
- 그룹스터디워크샵
- 커리어멘토링
- be
- 카카오API
- 국비지원취업
- 야놀자X패스트캠퍼스부트캠프
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |