codingstairs
노트에듀라이프연락
⌕검색⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

연락하기

로그인 없이도 보낼 수 있어요. 답변이 필요하면 이메일을 함께 적어 주세요.

  • 익명 폼으로 의견 남기기 →
  • ✉ warragon112@gmail.com
  • 카카오톡 오픈채팅 ↗

© 2026 codingstairs

  • 노트
  • 에듀
  • 검색
  • 라이프
  • 연락
  • 약관
  • RSS
  • GitHub
에듀›PostgreSQL 깊게 다루기 + Redis · Kafka›1단계

1단계

PostgreSQL 심화 — EXPLAIN · 인덱스

0회 조회

PostgreSQL 심화 — EXPLAIN · 인덱스

SELECT 느린 쿼리는 대부분 인덱스 문제. EXPLAIN 한 줄만 읽어도 50% 는 해결.

1. EXPLAIN ANALYZE

EXPLAIN ANALYZE SELECT * FROM posts WHERE user_id = 123 ORDER BY created_at DESC LIMIT 20;

출력:

Limit  (cost=0.42..8.44 rows=20) (actual time=0.023..0.187 rows=20 loops=1)
  ->  Index Scan using idx_posts_user_created on posts
        (cost=0.42..812.04 rows=2030) (actual time=0.023..0.183 rows=20)
        Index Cond: (user_id = 123)
  • Index Scan — 인덱스 사용 ✓
  • Seq Scan — 전체 테이블 스캔 ✗ (큰 테이블에서 느림)
  • actual time — 실제 실행 시간

2. 인덱스가 동작하지 않는 패턴

-- 함수 감싸기
WHERE LOWER(email) = 'a@b.c'         -- idx_email 무용
-- → 함수형 인덱스 필요: CREATE INDEX ON users (LOWER(email))

-- 앞부분 와일드카드
WHERE name ILIKE '%kim%'             -- btree 무용
-- → pg_trgm GIN 인덱스

-- 타입 변환
WHERE user_id::text = '123'          -- 인덱스 무용
-- → 타입 맞추기

-- OR
WHERE a = 1 OR b = 2                 -- 각 컬럼 따로 인덱스여도 비효율
-- → UNION 으로 분해 또는 복합 인덱스

3. 복합 인덱스 — 컬럼 순서

CREATE INDEX ON posts (user_id, created_at DESC);

왼쪽부터 매칭 가능:

WHERE user_id = 1                         -- ✓ 사용
WHERE user_id = 1 AND created_at > ...    -- ✓ 사용
WHERE created_at > ...                    -- ✗ 사용 안 됨

선행 컬럼 순서 = 선택도 높은 · 등식 쿼리.

4. Covering Index

CREATE INDEX ON posts (user_id, created_at DESC) INCLUDE (title);

INCLUDE 로 컬럼 추가. 쿼리가 인덱스만으로 답변 가능 (Index Only Scan).

5. 부분 인덱스

CREATE INDEX idx_published_posts ON posts (created_at DESC)
WHERE published = true;

95% 가 published = true 면 부분 인덱스가 작고 빠름.

6. GIN / GiST — 특수 인덱스

인덱스 용도
btree 기본 (등식 · 범위 · 정렬)
hash 등식 전용 (btree 로 대체 권장)
GIN 배열 · JSONB · 전문검색 (tsvector) · trigram
GiST 지리 (PostGIS) · 범위
BRIN 시계열 거대 테이블 (블록 범위)
CREATE INDEX ON posts USING gin (tags);            -- text[]
CREATE INDEX ON posts USING gin (content gin_trgm_ops);  -- 부분 일치
CREATE INDEX ON posts USING gin (metadata);        -- JSONB

7. 통계 · ANALYZE

ANALYZE posts;

PostgreSQL 이 쿼리 계획 세우는 기반. 대량 변경 후 수동 실행 권장. autovacuum 이 자동 처리하지만 지연될 수 있음.

8. 느린 쿼리 로그

postgresql.conf:

log_min_duration_statement = 1000     # 1초 이상 로그

실제로 어떤 쿼리가 느린지 측정 없이 추측 금지.

9. pg_stat_statements

CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

SELECT query, calls, total_exec_time, mean_exec_time
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;

누적 가장 느린 쿼리 TOP 20. 최적화 1 순위.

10. 자주 걸리는 자리

  • 인덱스 많이 만들면 쓰기 느림 — 각 INSERT/UPDATE 가 모든 인덱스 갱신
  • UUID 컬럼 btree — 랜덤이라 insert 시 페이지 조각. UUID v7 (시계열) 고려
  • NULL 값 많은 컬럼 인덱스 — 의미 있으면 부분 인덱스
  • VACUUM 안 하면 — 통계 낡아 잘못된 계획 · dead tuple

하고픈 말

"느린 쿼리 → EXPLAIN → 인덱스 확인" 세 단계 반복이 DB 튜닝의 기본기. 추측보다 측정.

Next

  • 02-multi-pool-orchestration

2단계 →

여러 풀 오케스트레이션