성능 장애는 계층별 지표를 함께 봐야 근본 원인을 찾을 수 있다

성능 문제는 애플리케이션 레이어 하나만 봐서는 원인을 찾기 어렵다. CPU가 여유 있는데 응답이 느리면 DB에 문제가 있을 수 있고, DB 쿼리는 빠른데 지연이 생기면 JVM GC가 원인일 수 있다. 계층별로 지표를 함께 보는 이유가 여기 있다.

기본적으로 챙겨야 할 지표들

  • 시스템 레벨: CPU 코어별 사용률, 메모리 힙 상태 + GC 패턴, 디스크 I/O
  • 네트워크: 응답 시간, 처리량
  • 애플리케이션: TPS, 에러율, 응답 시간 분포

이 중 하나라도 이상 신호가 보이면 그 계층부터 깊게 파고 들어간다.

실전에서 자주 마주친 상황들

DB 쿼리가 느릴 때

SELECT * 처럼 불필요한 컬럼까지 가져오는 쿼리, 인덱스를 타지 못하는 조건, 적절한 LIMIT 없이 전체를 스캔하는 쿼리가 주범인 경우가 많다.

-- 느린 쿼리: 불필요한 컬럼 전체 조회, LIMIT 없음
SELECT * FROM orders o
JOIN order_items oi ON o.id = oi.order_id
WHERE o.created_at > '2023-01-01';
 
-- 개선: 필요한 컬럼만, 조건 추가, LIMIT 설정
SELECT o.id, o.status, oi.product_id
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
WHERE o.created_at > '2023-01-01'
AND o.status = 'COMPLETED'
LIMIT 1000;

JVM GC 튜닝이 필요할 때

Full GC가 자주 발생하거나 GC 시간이 길면 응답 지연의 직접적인 원인이 된다. G1GC로 전환하고 GC 로그를 남겨 패턴을 분석하는 게 출발점이다.

-XX:+UseG1GC
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/var/log/gc.log

대용량 배치 처리가 느릴 때

건당 처리에서 병렬 처리로 전환하면 처리량을 크게 높일 수 있다. 단, 스레드 풀 크기는 서버 리소스에 맞게 설정해야 한다.

@Scheduled(fixedRate = 1000)
public void processBatch() {
    List<Data> dataList = repository.findLimit(1000);
    ExecutorService executor = Executors.newFixedThreadPool(5);
    for (Data data : dataList) {
        executor.submit(() -> processData(data));
    }
    executor.shutdown();
}

캐시가 필요할 때

조회 빈도가 높고 변경이 드문 데이터라면 캐시를 도입한다. Spring의 @Cacheable을 활용하면 DB 부하를 줄이면서 응답 속도를 개선할 수 있다.

@Cacheable(value = "products", key = "#id")
public Product getProduct(Long id) {
    return productRepository.findById(id)
        .orElseThrow(() -> new ProductNotFoundException(id));
}

모니터링 체계 구성

Prometheus + Grafana 조합이 현재 가장 많이 쓰이는 방법이다. Spring Actuator와 연동하면 JVM, DB 커넥션 풀, HTTP 요청 지표를 한 곳에서 확인할 수 있다.

scrape_configs:
  - job_name: 'spring-actuator'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['localhost:8080']

성능 장애를 맞닥뜨렸을 때 가장 위험한 건 특정 계층만 보고 결론을 내리는 것이다. “DB 느리네, 쿼리 튜닝하면 되겠다”로 시작했다가 실제 원인이 GC 정지였던 경우도 있다. 지표를 계층별로 같이 보는 습관이 그 방황을 줄여준다.

연결 (이유)

출처(참고문헌)