빠른 비교
| 인덱스 | 잘 맞는 데이터 | 대표 연산 |
|---|---|---|
| B-tree | 일반 scalar 값 | =, 범위, 정렬 |
| GIN | 배열, JSONB, 전문 검색 | 포함, 존재, 토큰 검색 |
| GiST | 공간, 범위, 유사도 | 겹침, 거리, 범위 |
| BRIN | 물리 순서와 값이 비슷한 큰 테이블 | 대용량 range scan |
CREATE INDEX idx_users_email ON users USING btree (email);
CREATE INDEX idx_docs_tags ON docs USING gin (tags);
CREATE INDEX idx_logs_created_at_brin ON logs USING brin (created_at);갈리는 기준
B-tree는 기본값이자 가장 넓은 선택지다
PostgreSQL에서 USING을 생략하고 CREATE INDEX를 만들면 보통 B-tree가 쓰입니다. 동등 비교, 범위 조건, 정렬, unique 제약에 폭넓게 맞습니다.
CREATE INDEX idx_orders_created_at
ON orders (created_at DESC);
SELECT *
FROM orders
WHERE created_at >= NOW() - INTERVAL '7 days'
ORDER BY created_at DESC;일반적인 ID, email, timestamp, foreign key 조회는 B-tree를 먼저 검토합니다. 다른 인덱스 방식은 데이터 구조나 연산자가 B-tree로 해결되지 않을 때 선택합니다.
GIN은 한 행 안에 여러 검색 항목이 있을 때 강하다
GIN은 하나의 행이 여러 key를 품는 데이터에 적합합니다. 배열 원소, JSONB key/value, tsvector 토큰처럼 내부 요소를 역색인하는 방식입니다.
CREATE INDEX idx_articles_search
ON articles USING gin (search_vector);
CREATE INDEX idx_products_attrs
ON products USING gin (attributes jsonb_path_ops);GIN은 읽기 검색에는 강하지만 쓰기 비용과 인덱스 크기가 커질 수 있습니다. 자주 갱신되는 JSONB나 배열 컬럼에 무조건 GIN을 붙이면 쓰기 부하가 커질 수 있습니다.
특수 인덱스
GiST는 범위, 공간, nearest-neighbor 성격의 검색에 쓰입니다. geometry 확장이나 range type, exclusion constraint처럼 “겹치는가”, “가까운가”를 묻는 문제에 자주 등장합니다.
CREATE INDEX idx_bookings_period
ON bookings USING gist (during);BRIN은 매우 큰 테이블에서 값이 물리 저장 순서와 어느 정도 같이 움직일 때 효과적입니다. 예를 들어 append-only 로그의 created_at처럼 뒤에 쌓일수록 시간이 커지는 컬럼은 작은 BRIN 인덱스로 넓은 범위를 빠르게 제외할 수 있습니다.
CREATE INDEX idx_logs_created_at_brin
ON logs USING brin (created_at);선택 기준
| 상황 | 선택 |
|---|---|
| 일반 조건 조회와 정렬 | B-tree |
| JSONB 포함 검색 | GIN |
| 배열에 특정 원소 포함 | GIN |
전문 검색 tsvector | GIN |
| range overlap, 공간 검색 | GiST |
| 시간순 append 대용량 로그 | BRIN |
주의할 점
인덱스 type은 컬럼 타입만 보고 고르면 안 됩니다. 실제 WHERE 연산자, 정렬, 반환 행 비율, 쓰기 빈도까지 함께 봐야 합니다.
GIN, GiST, BRIN은 B-tree를 대체하는 범용 상위 호환이 아닙니다. 해결하는 질문이 다르므로 EXPLAIN (ANALYZE, BUFFERS)로 실제 실행 계획과 I/O를 확인하세요.
참고 링크
3 sources