이 글에서 얻는 것

  • SQL을 “문법”이 아니라 **실행 관점(어떤 순서로 처리되는지)**으로 이해합니다.
  • 조인/집계/정렬에서 성능이 터지는 지점을 미리 예상할 수 있습니다(왜 느린지 말로 설명 가능).
  • 인덱스가 잘 타는 조건(sargable)과 안 타는 조건을 구분해, 다음 인덱스/튜닝 글을 훨씬 쉽게 따라갈 수 있습니다.

0) 왜 SQL ‘기본’이 중요한가

백엔드 성능 이슈의 상당수는 결국 SQL로 귀결됩니다.

  • 애플리케이션은 느린 DB를 숨기기 어렵고,
  • 캐시/비동기/샤딩 같은 고급 해법도 “기본 쿼리”가 망가지면 효과가 제한됩니다.

그래서 이 글은 “정답 쿼리”가 아니라, 느린 쿼리를 보고 원인을 좁히는 감각을 만드는 데 집중합니다.

1) SQL 실행 순서(논리적 처리 순서)

보통 SELECT는 아래 순서로 처리된다고 생각하면 디버깅이 쉬워집니다.

  1. FROM / JOIN(데이터 집합 만들기)
  2. WHERE(필터)
  3. GROUP BY(그룹)
  4. HAVING(그룹 필터)
  5. SELECT(컬럼 계산/프로젝션)
  6. ORDER BY(정렬)
  7. LIMIT/OFFSET(잘라내기)

포인트:

  • WHERE는 “행”을 줄이고, HAVING은 “그룹”을 줄입니다.
  • ORDER BY는 보통 비쌉니다(정렬 비용). 인덱스로 정렬을 대체할 수 있으면 큰 이득입니다.

2) 조인: 무엇이 느려지는가

2-1) 조인의 비용은 “매칭”에서 온다

조인은 결국:

  • A의 각 행에 대해,
  • B에서 매칭되는 행을 찾는 과정입니다.

그래서 핵심은:

  • 조인 키에 인덱스가 있는가?
  • 어느 쪽이 드라이빙 테이블(먼저 읽는 쪽)인가?
  • 필터(WHERE)가 얼마나 먼저 적용되는가?

2-2) 흔한 실수

  • 필터를 늦게 적용해 조인 대상이 불필요하게 커짐
  • 조인 키 타입 불일치(문자열 vs 숫자 등)로 인덱스가 무력화
  • 1:N 조인 후 중복 행이 폭발해 DISTINCT/GROUP BY로 땜질(근본 문제 숨김)

3) 집계/정렬: 데이터가 커질수록 폭발한다

3-1) GROUP BY

GROUP BY는 “모아서 계산”이므로 데이터가 커지면 비용이 빠르게 증가합니다.

  • 그룹 키가 많을수록(고유값 많을수록) 비용 증가
  • 정렬/해시 기반 집계는 DB마다 다르지만, 공통적으로 “메모리/임시 공간”이 관여합니다

3-2) ORDER BY

정렬은 다음 중 하나입니다.

  • 인덱스 순서를 그대로 이용(싸다)
  • 별도의 정렬 수행(비싸다, 임시 공간/파일 정렬)

그래서 “인덱스가 ORDER BY를 커버할 수 있는지”는 튜닝에서 자주 보는 포인트입니다.

4) 인덱스가 ‘먹는’ 조건(sargable) 감각

인덱스를 타기 쉬운 조건:

  • col = ?, col IN (...)
  • col >= ? AND col < ?(범위)

인덱스를 망치기 쉬운 조건:

  • 컬럼에 함수 적용: DATE(col) = ...
  • 앞 와일드카드: LIKE '%abc'
  • 타입 캐스팅/불일치로 인해 변환이 발생

예:

-- 인덱스를 못 타기 쉬움(컬럼에 함수)
WHERE DATE(created_at) = '2025-12-16'

-- 범위로 바꾸면 인덱스를 타기 쉬움
WHERE created_at >= '2025-12-16 00:00:00'
  AND created_at <  '2025-12-17 00:00:00'

5) EXPLAIN을 읽는 최소 루틴(맛보기)

다음 글에서 더 깊게 보겠지만, 최소한 아래는 습관으로 가져가면 좋습니다.

  • scan 범위가 너무 큰가(풀스캔/범위 스캔)
  • 어떤 인덱스를 썼는가(의도한 인덱스가 맞는가)
  • 정렬/임시 테이블이 있는가(정렬 비용/메모리/디스크 사용)

중요한 건:

EXPLAIN은 “정답”이 아니라, 가설을 세우게 해주는 힌트다

연습(추천)

  • 같은 조건을 “함수 조건”과 “범위 조건”으로 각각 작성하고, EXPLAIN에서 인덱스 사용 여부가 어떻게 달라지는지 비교해보기
  • 1:N 조인에서 결과가 폭발하는 케이스를 만들고, 필터를 조인 전에 적용했을 때 얼마나 줄어드는지 실험해보기
  • ORDER BY가 있는 조회에서 “인덱스로 정렬이 대체되는 경우/안 되는 경우”를 케이스로 만들어 비교해보기