쿼리 계획

Nest 쿼리 최적화 판단

select를 줄이고 index를 추가한다는 말만으로는 병목을 찾을 수 없다. TypeORM이나 Prisma가 만든 실제 SQL, EXPLAIN 결과, N+1 호출 수, offset과 cursor pagination의 비용, slow query 기준을 함께 봐야 한다.

관찰 지표 SQL log, query count, duration으로 느린 요청의 위치를 좁힌다
실행 계획 EXPLAIN에서 scan, sort, row estimate가 개선되는지 확인한다
조회 패턴 N+1, pagination, join 방식을 데이터 크기와 응답 기준에 맞춘다
01

느린 요청 찾기

endpoint별 p95 latency와 slow query log를 연결해 문제 요청을 고른다.

전체 평균보다 느린 꼬리를 본다
02

SQL 확인

ORM 메서드가 실제로 만든 SQL과 바인딩 값을 로그로 확인한다.

repository 코드만으로 실행 비용을 알 수 없다
03

실행 계획 읽기

Seq Scan, Sort, Nested Loop, index 사용 여부를 EXPLAIN으로 본다.

인덱스가 있어도 쓰이지 않을 수 있다
04

N+1 제거

목록 하나가 관계 조회 여러 번으로 번지는지 query count를 센다.

eager loading과 batch loading을 구분한다
05

페이지네이션 선택

큰 offset이 느려지는 목록은 cursor 기반으로 바꿀 수 있는지 검토한다.

정렬 key의 안정성이 필요하다
Index
검색 조건 지원 where, order by, join 조건과 맞는 인덱스인지 확인한다.
무작정 추가하면 쓰기 비용이 늘어난다
N+1
반복 관계 조회 목록 크기에 비례해 쿼리 수가 늘면 batch나 join 전략을 바꾼다.
로그로 쿼리 수를 센다
Offset
깊은 페이지 비용 큰 offset은 앞 행을 건너뛰는 비용이 커질 수 있다.
cursor는 정렬 기준이 필요하다
Projection
필드 축소 필요한 컬럼만 가져오되 relation과 DTO 계약이 깨지지 않게 한다.
과한 select는 유지보수를 어렵게 할 수 있다

DB 확인

실제 SQL 최적화 전후 ORM이 만든 SQL을 저장해 비교한다.
실행 계획 EXPLAIN에서 scan, sort, row estimate가 개선됐는지 확인한다.
요청 수 목록 크기가 늘어도 쿼리 수가 선형 폭증하지 않는지 본다.