홈/엔지니어/데이터베이스/PostgreSQL 인덱스 최적화 — 느린 쿼리 진…
데이터베이스고급UbuntuDebianCentOSPostgreSQL인덱스쿼리최적화

PostgreSQL 인덱스 최적화 — 느린 쿼리 진단과 인덱스 전략

EXPLAIN ANALYZE로 느린 쿼리를 찾고, 적절한 인덱스 타입을 선택하여 성능을 개선하는 방법을 실무 중심으로 정리합니다.

목차
  1. 느린 쿼리 찾기
  2. EXPLAIN ANALYZE 읽기
  3. 인덱스 생성
  4. 인덱스 타입 선택
  5. 인덱스 상태 확인
  6. VACUUM과 통계 갱신

느린 쿼리 찾기

SQL
-- pg_stat_statements 활성화 (postgresql.conf)
shared_preload_libraries = 'pg_stat_statements'

-- 설치
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- 상위 10개 느린 쿼리
SELECT
  round(total_exec_time::numeric, 2) AS total_ms,
  calls,
  round(mean_exec_time::numeric, 2) AS avg_ms,
  left(query, 100) AS query_preview
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;

EXPLAIN ANALYZE 읽기

SQL
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT * FROM orders
WHERE user_id = 123 AND status = 'pending'
ORDER BY created_at DESC
LIMIT 10;

핵심 항목:

항목의미
Seq Scan전체 테이블 스캔 (인덱스 없음)
Index Scan인덱스 사용
Bitmap Heap Scan여러 인덱스 조합
actual time=0.1..5.2실제 실행 시간 (ms)
Buffers: shared hit=8캐시 히트 (miss가 많으면 I/O 문제)

인덱스 생성

SQL
-- 단일 컬럼
CREATE INDEX idx_orders_user_id ON orders (user_id);

-- 복합 인덱스 (선택성 높은 것 먼저)
CREATE INDEX idx_orders_user_status ON orders (user_id, status);

-- 부분 인덱스 (조건에 맞는 행만 인덱싱)
CREATE INDEX idx_orders_pending ON orders (created_at)
WHERE status = 'pending';

-- 표현식 인덱스
CREATE INDEX idx_users_email_lower ON users (lower(email));

-- 온라인 생성 (잠금 없이)
CREATE INDEX CONCURRENTLY idx_posts_category ON posts (category);

인덱스 타입 선택

SQL
-- B-tree: 기본, 범위 쿼리 (=, <, >, BETWEEN, LIKE 'prefix%')
CREATE INDEX idx_b ON events (created_at);

-- Hash: = 조건만, 빠름
CREATE INDEX idx_h ON sessions USING HASH (session_token);

-- GIN: 배열, JSONB, 전문 검색
CREATE INDEX idx_gin ON posts USING GIN (tags);
CREATE INDEX idx_jsonb ON products USING GIN (metadata jsonb_path_ops);

인덱스 상태 확인

SQL
-- 테이블의 인덱스 목록과 사용 횟수
SELECT
  indexname,
  pg_size_pretty(pg_relation_size(indexname::regclass)) AS size,
  idx_scan AS scans
FROM pg_stat_user_indexes
WHERE relname = 'orders'
ORDER BY idx_scan;

-- 사용되지 않는 인덱스 (제거 검토)
SELECT schemaname, relname, indexrelname, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0 AND schemaname = 'public';

VACUUM과 통계 갱신

SQL
-- 통계 갱신 (플래너가 올바른 실행계획 선택)
ANALYZE orders;

-- VACUUM + ANALYZE
VACUUM ANALYZE orders;

-- 자동 VACUUM 상태 확인
SELECT * FROM pg_stat_user_tables WHERE relname = 'orders';
#PostgreSQL#인덱스#쿼리최적화#성능#SQL
편집 안내 · Editorial Note

AI 도구가 초안 작성에 사용될 수 있습니다. 명령어의 실행 여부와 검증 환경은 각 가이드에 기재된 근거를 확인하세요. 적용 전 사용 중인 버전의 공식 문서를 확인하고, 오류는 이메일로 알려주세요. 이메일로 제보

주제 관련 공식 문서(자동 연결 · 문장별 근거 아님)PostgreSQL 공식 문서 ↗

질문 & 답변 (Q&A)

이 가이드에 대해 궁금한 점을 질문해보세요. 확인 후 답변드립니다.