stack-analysis
CHAPTER 39 / 90
읽기 약 2분
FUNCTION
PostgreSQL 성능 튜닝: EXPLAIN ANALYZE
핵심 개념
EXPLAIN·인덱스 사용 확인·Seq Scan vs Index Scan — 느린 쿼리 30배 가속.
본문
EXPLAIN ANALYZE 기본
EXPLAIN ANALYZE
SELECT u.name, COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON p.author_id = u.id
WHERE u.created_at > '2024-01-01'
GROUP BY u.id, u.name
ORDER BY post_count DESC
LIMIT 10;
-- 출력
HashAggregate (cost=15234.50..15234.60 rows=10 width=72)
(actual time=234.567..234.580 rows=10 loops=1)
-> Hash Right Join (cost=2456.78..14567.12 rows=8901 width=64)
(actual time=12.345..201.234 rows=8765 loops=1)
Hash Cond: (p.author_id = u.id)
-> Seq Scan on posts p (cost=0.00..1234.56 rows=43210)
(actual time=0.012..50.123 rows=43210)
⚠️ Seq Scan = 풀 테이블 스캔
-> Hash (cost=2123.45..2123.45 rows=2345)
-> Index Scan using idx_users_created_at on users u
✅ 인덱스 사용
Planning Time: 0.234 ms
Execution Time: 234.789 ms핵심 지표 해석
| 지표 | 의미 | 좋음 |
|---|---|---|
| Seq Scan | 풀 테이블 스캔 | 작은 테이블만 |
| Index Scan | 인덱스 사용 | 일반적 ✅ |
| Index Only Scan | 인덱스만으로 답 | 최고 ✅✅ |
| Bitmap Heap Scan | OR 조건 결합 | 보통 |
| Nested Loop | 작은 테이블 join | 보통 |
| Hash Join | 큰 테이블 join | 좋음 |
| Merge Join | 정렬된 테이블 | 좋음 |
actual time vs cost:
- cost = planner 추정 (의미 없음)
- actual time = 실제 시간 (집중)
rows 차이:
- cost rows ≠ actual rows = 통계 오래됨 → ANALYZE 필요인덱스 추가로 30배 가속
-- 느림 (300ms)
SELECT * FROM orders WHERE user_id = 12345 AND status = 'pending';
-- 인덱스 생성
CREATE INDEX idx_orders_user_status ON orders(user_id, status);
-- 빠름 (10ms)
EXPLAIN ANALYZE
SELECT * FROM orders WHERE user_id = 12345 AND status = 'pending';
-- → Index Scan using idx_orders_user_status
ANALYZE orders; -- 통계 갱신부분 인덱스 (Partial Index)
-- 99%가 status='completed'면, 나머지 1%만 인덱싱
CREATE INDEX idx_orders_pending
ON orders(user_id)
WHERE status = 'pending';
-- 인덱스 크기 ↓ + 쿼리 속도 ↑쿼리 최적화 패턴
-- ❌ N+1 (애플리케이션 레벨)
for user in users:
posts = db.query("SELECT * FROM posts WHERE author_id = ?", user.id)
-- ✅ JOIN 한 번
SELECT u.*, p.*
FROM users u
LEFT JOIN posts p ON p.author_id = u.id
WHERE u.id IN (...);
-- ❌ SELECT *
SELECT * FROM products WHERE id = 1;
-- ✅ 필요한 컬럼만
SELECT id, name, price FROM products WHERE id = 1;
-- ❌ OFFSET 큰 값 (느림)
SELECT * FROM posts ORDER BY id OFFSET 10000 LIMIT 20;
-- ✅ Cursor (id 기반)
SELECT * FROM posts WHERE id > 10000 ORDER BY id LIMIT 20;CTE vs 서브쿼리
-- CTE — 가독성 좋음
WITH active_users AS (
SELECT * FROM users WHERE last_seen_at > NOW() - INTERVAL '7 days'
)
SELECT au.name, COUNT(p.id)
FROM active_users au
LEFT JOIN posts p ON p.author_id = au.id
GROUP BY au.id, au.name;
-- PostgreSQL 12+ CTE는 자동 inline (성능 동일)
-- 그 이전 버전은 MATERIALIZED 명시 필요슬로우 쿼리 모니터링
-- pg_stat_statements 익스텐션
CREATE EXTENSION pg_stat_statements;
-- TOP 10 느린 쿼리
SELECT query, calls, mean_exec_time, total_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;
-- postgresql.conf
log_min_duration_statement = 1000 -- 1초+ 쿼리 로깅다음 챕터
CH.40 "인덱스 전략: B-tree/GIN/GiST 선택".
AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude
무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6
내 코드의 PostgreSQL 튜닝 부분을 분석해서 실전 분석 + 개선 우선순위를 알려줘.
ChatGPT
무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro
PostgreSQL 튜닝 관련 인기 라이브러리/패턴 5개를 비교 분석해서 패턴 추출를 알려줘.
Gemini
무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro
내 프로젝트 전체에서 PostgreSQL 튜닝 최적화 가능 위치를 보고해줘.
Grok
무료: Grok 4.1 / SuperGrok $30/mo
2026년 한국 백엔드 시장의 PostgreSQL 튜닝 트렌드를 솔직히 알려줘.
⭐ 이것만 기억하세요
PostgreSQL 성능 튜닝: EXPLAIN ANALYZE는 이 3가지만 확실히 잡으세요
1.EXPLAIN ANALYZE의 actual time + Seq Scan 발견 = 인덱스 검토
2.복합 인덱스 (a, b) 순서 중요 — 자주 필터되는 컬럼 먼저
3.OFFSET 큰 값 = 느림 → cursor 기반 페이지네이션
공유하기
진행도 39 / 90