같은 요청인데 왜 87ms와 6.94초를 오갈까
MyBatisMariaDBPerformanceN+1
01이슈 및 현상
usageTree 조회 API가 완전히 동일한 파라미터인데도 실행 시점에 따라 응답시간이 87ms에서 6.94초까지, 최대 80배 이상 차이 났습니다.
02원인 파악
서비스가 energy_code별 계통(facility) 목록을 조회한 뒤 계통 하나하나마다 데이터·비교 쿼리를 순차 호출하는 N+1 구조였습니다. '전체' 조회 시 건물당 최대 67개 계통을 순회했고, 여기에 283만 row 테이블의 버퍼풀 cold/warm 상태가 겹치면서 디스크 I/O 누적 편차가 계통 수만큼 증폭됐습니다. (인덱스 부재·배치 락 경합 등 다른 가설은 확인 후 배제)
03해결 방안
계통별 순차 호출을 energy_system_id 리스트를 받는 IN 절 배치 쿼리로 묶어, 요청당 쿼리 횟수를 '계통 개수 × 2'에서 'energy_code 개수 × 2'(사실상 상수)로 줄이도록 설계했습니다. 반환된 flat 결과는 energy_system_id 기준으로 그룹핑해 기존 응답 스키마를 그대로 유지합니다.
04결과 및 성과
쿼리 횟수 자체가 상수 수준으로 줄어 콜드 캐시 상태에서도 디스크 I/O 누적이 크게 감소하고, 커넥션 점유 시간이 짧아져 동시 요청이 몰릴 때의 지연까지 완화될 것으로 기대합니다. 응답 스키마를 유지해 프론트 변경 없이 매퍼·서비스만 교체하는 소규모 PR로 롤아웃할 수 있습니다.