PostgreSQL
강의

#11 - EXPLAIN ANALYZE(큰 테이블이 작은 테이블을 만났을 때, DB는 어떻게 행동할까?)

중년개발자
중년개발자

@loxo

8일 전

15

postgresql02.jpg

실행 계획을 이해하는 감각 만들기

이제부터는 EXPLAIN ANALYZE를 왜 이렇게 나왔는지 납득하는 단계로 들어간다. 숫자와 용어를 외우는 게 아니라, 데이터 크기와 접근 방식의 이야기로 이해하면 훨씬 편하다.


Seq Scan이 선택되는 진짜 이유

Seq Scan은 말 그대로 테이블을 처음부터 끝까지 읽는 방식이다.

많이들 이렇게 생각한다.

Seq Scan = 인덱스를 못 써서 생긴 문제

하지만 PostgreSQL 입장에서는 전혀 다르다.

PostgreSQL이 Seq Scan을 고르는 논리

DB는 항상 이렇게 계산한다.

  • 인덱스를 타면

    • 인덱스 페이지 읽고
    • 테이블 페이지 다시 읽고
    • 랜덤 I/O가 많이 발생
  • Seq Scan을 하면

    • 테이블 페이지를 순서대로 쭉 읽음
    • 디스크, 캐시 입장에서 매우 효율적

그래서:

  • 전체 row 중 많은 비율을 읽어야 한다면
  • 인덱스가 있어도 Seq Scan이 더 빠를 수 있다

실생활 비유

책에서 단어 하나 찾기:

  • 단어가 2~3개만 필요 → 목차(인덱스) 사용
  • 책의 절반 이상을 봐야 함 → 처음부터 쭉 읽는 게 빠름

DB도 똑같다.


Index Scan vs Bitmap Scan 차이

둘 다 인덱스를 쓰지만, 데이터를 가져오는 전략이 다르다.

Index Scan

  • 인덱스에서 row 위치 하나 찾음
  • 바로 테이블 페이지로 이동
  • 다시 인덱스로 돌아감

특징:

  • 조건에 맞는 row가 아주 적을 때 최고
  • 랜덤 I/O가 많아질 수 있음

Bitmap Scan

  • 인덱스를 먼저 쭉 스캔
  • 필요한 row들의 위치를 비트맵(목록) 으로 모음
  • 테이블 페이지를 묶어서 한 번에 읽음

특징:

  • 조건에 맞는 row가 어느 정도 많을 때 유리
  • 페이지 단위 접근이라 효율적

실생활 비유

  • Index Scan → 필요한 물건 하나하나 직접 집으러 다님
  • Bitmap Scan → 쇼핑 리스트 먼저 만들고, 동선 최적화해서 한 번에 이동

PostgreSQL은 row 수와 페이지 분포를 보고 둘 중 하나를 고른다.


Nested Loop / Hash Join 감각 잡기

조인은 row 수의 곱셈 문제다. 이 감각이 없으면 실행 계획이 안 보인다.

Nested Loop

구조:

  • 바깥 테이블 row 하나
  • 안쪽 테이블을 매번 다시 탐색

그래서:

  • 바깥 row가 적고
  • 안쪽에 인덱스가 있을 때 매우 빠르다

하지만:

  • 바깥 10만 row × 안쪽 탐색
  • → 순식간에 재앙

Hash Join

구조:

  • 작은 테이블을 메모리에 올려 해시 테이블 생성
  • 큰 테이블을 한 번 훑으며 매칭

그래서:

  • 대량 데이터에 안정적
  • 인덱스가 없어도 동작

단점:

  • 메모리(work_mem) 부족하면 느려짐

직관적인 기준

  • 소량 × 인덱스 → Nested Loop
  • 대량 × 대량 → Hash Join

실행 계획이 갑자기 바뀌는 이유

어제까지 빠르던 쿼리가 오늘 느려지는 이유는 대부분 이것이다.

  1. 데이터 양이 크게 변함
  2. 특정 값으로 데이터가 몰림
  3. 통계 정보가 오래됨
  4. 파라미터 값이 달라짐

PostgreSQL은 통계 기반 DB다. 통계가 현실과 어긋나면, 잘못된 선택을 한다.

그래서 ANALYZE가 중요하다.


actual time vs Execution Time 차이

  • actual time → 각 노드가 실제로 걸린 시간
  • Execution Time → 쿼리 전체가 끝난 시간

주의할 점:

  • actual time은 노드별로 중복되어 보일 수 있다
  • 성능 판단 기준은 항상 Execution Time

loops 값이 의미하는 것

loops는:

이 노드가 몇 번 실행되었는지

Nested Loop에서 특히 중요하다.

예:

  • loops=1 → 한 번 실행
  • loops=10000 → 만 번 반복

이때 actual time이 작아 보여도:

  • time × loops = 실제 비용

그래서 loops는 숨겨진 폭탄이다.


EXPLAIN ANALYZE 결과 실제 예제 해부

sql
EXPLAIN ANALYZE SELECT * FROM orders o JOIN users u ON o.user_id = u.id WHERE u.country = 'KR';

해석 흐름:

  1. users 테이블에서 country='KR' 조건

    • row 수 확인
    • Index Scan or Seq Scan 확인
  2. 해당 users row를 기준으로

    • orders를 어떻게 조인하는지 확인
  3. Nested Loop이면

    • users row 수 × orders 접근 횟수 확인
  4. loops 값과 actual rows를 함께 본다

이 순서로 보면 실행 계획이 문장처럼 읽히기 시작한다.


핵심 정리

  • 실행 계획은 숫자가 아니라 이야기
  • row 수가 모든 선택의 출발점
  • 인덱스는 page 단위로 움직인다
  • loops는 곱셈이다
  • DB는 항상 "가장 싸 보이는 길"을 고른다

EXPLAIN ANALYZE를 읽는다는 것은 PostgreSQL의 판단을 이해하는 것이다

#PostgreSQL#EXPLAIN ANALYZE#실행 계획#Seq Scan#인덱스 전략

댓글 0

Ctrl + Enter를 눌러 등록할 수 있습니다
※ AI 다듬기는 내용을 정제하는 보조 기능이며, 최종 내용은 사용자가 확인해야 합니다.