6.3 GiST와 SP-GiST 인덱스
GiST(Generalized Search Tree)와 SP-GiST(Space-Partitioned GiST)는 B-tree로는 다루기 어려운 데이터 타입 — 공간 좌표·범위 타입·IP 네트워크·기하 도형·full-text vector 등 — 을 인덱싱하는 일반화된 트리 인덱스입니다. 운영자는 PostGIS·범위 쿼리 작업에서 가장 자주 마주칩니다.
GiST 개요
GiST는 balanced search tree의 framework다. 새 데이터 타입의 비교·containment 연산을 정의하면 해당 타입을 인덱싱할 수 있게 해 주는 일반 구조 — “확장 가능한 B-tree"라고 보면 됩니다.
| 잘 처리하는 쿼리 | 예시 |
|---|---|
| 공간 containment | WHERE geom && ST_MakeEnvelope(...) (PostGIS) |
| 범위 타입 overlap | WHERE tsrange && '[2026-01-01, 2026-02-01)'::tsrange |
| nearest neighbor | ORDER BY point <-> POINT '(1,2)' LIMIT 10 (KNN) |
| IP/network 포함 | WHERE inet_col >> '10.0.0.0/8' |
| full-text similarity | trigram WHERE col % 'query' (pg_trgm) |
| 도형 교차 | WHERE box1 && box2 |
GiST 인덱스 만들기
-- 범위 타입 — 회의실 예약 충돌
CREATE INDEX idx_meetings_when ON meetings USING gist (when_range);
-- 충돌 쿼리
SELECT * FROM meetings
WHERE when_range && '[2026-05-23 10:00, 2026-05-23 11:00)'::tsrange;-- PostGIS geometry
CREATE INDEX idx_parcels_geom ON parcels USING gist (geom);-- KNN search (가장 가까운 N개)
CREATE INDEX idx_points ON places USING gist (location);
SELECT name FROM places ORDER BY location <-> POINT '(127.0, 37.5)' LIMIT 5;EXCLUSION CONSTRAINT
같은 시간대 충돌을 DB 수준에서 막는 강력한 패턴:
CREATE TABLE meetings (
id serial PRIMARY KEY,
room_id int,
when_range tsrange,
EXCLUDE USING gist (
room_id WITH =,
when_range WITH &&
)
);같은 room_id + 시간 범위가 겹치는 row 두 개가 동시에 존재할 수 없도록 보장. INSERT가 충돌하면 PostgreSQL이 자동 거부합니다. row-level 락 없이 일관성 유지합니다.
buffering / fillfactor
큰 데이터로 GiST를 처음 만들 때 buffering = on 옵션을 쓰면 빌드가 빨라진다:
CREATE INDEX idx_big_geom ON big_table USING gist (geom)
WITH (buffering = on, fillfactor = 90);fillfactor는 B-tree와 비슷 — 페이지 분할 빈도와 BLOAT 트레이드오프.
SP-GiST 개요
SP-GiST는 공간 분할 트리(quad-tree, k-d tree, radix tree, suffix tree 등)를 일반화한 인덱스. GiST와 비슷한 데이터 타입을 다루지만 부모-자식이 겹치지 않는 분할이 핵심이라 일부 쿼리에서 더 빠릅니다.
| 잘 처리하는 쿼리 | 예시 |
|---|---|
| Point in box | WHERE point <@ box |
| IP 라우팅 트리 | WHERE inet_col <<= '10.0.0.0/8' |
| text prefix(트리/radix) | WHERE textcol LIKE 'foo%' (text_ops opclass) |
CREATE INDEX idx_ips_spgist ON connections USING spgist (src_ip);GiST vs SP-GiST 선택
| 측면 | GiST | SP-GiST |
|---|---|---|
| 트리 구조 | balanced, 자식 영역 겹침 가능 | unbalanced, 자식 영역 겹침 없음 |
| 강한 분야 | overlap·containment·KNN | point lookup, prefix·radix |
| BLOAT 회복 | REINDEX | REINDEX |
| Index-only scan | 일부 가능 | 일부 가능 |
| WAL 지원 | 있음 | 있음 (PG 11+) |
| 운영 도구 지원 | 더 많음 | 적음 |
의사 결정 순서:
- 공간·범위·KNN — GiST부터
- 정확 point lookup 또는 명확한 비겹침 분할 가능 — SP-GiST가 더 빠를 수 있음
- 둘 다 후보면 GiST를 기본, 성능 측정 후 SP-GiST 검토
보조 확장과 짝
| 확장 | GiST/SP-GiST 활용 |
|---|---|
| PostGIS | geometry, geography, raster 모두 GiST |
| btree_gist | int·timestamp 등 B-tree 타입을 GiST에 끼워 넣어 exclusion constraint 조합 가능 |
| pg_trgm | 텍스트 trigram — LIKE '%foo%'·유사도 검색 (GIN도 가능) |
| cube | 다차원 공간 (KNN) |
btree_gist 예시 — room_id(int)와 when_range를 한 EXCLUSION에 같이:
CREATE EXTENSION btree_gist;
CREATE TABLE meetings (
...,
EXCLUDE USING gist (room_id WITH =, when_range WITH &&)
);btree_gist 없으면 room_id(int)에 = 연산자를 GiST가 못 다룹니다.
운영 시 주의
| 주의 | 메모 |
|---|---|
| GiST 빌드는 B-tree보다 느림 | 큰 테이블은 CONCURRENTLY + buffering = on 권장 |
| 인덱스 크기가 큼 | leaf entry에 키 외에 페이지 boundary 정보도 포함 |
VACUUM이 dead entry 정리 — BLOAT 대응 | REINDEX CONCURRENTLY 필요 |
KNN(ORDER BY x <-> y)는 GiST만 가능 | SP-GiST는 KNN 미지원 (PG 18 기준) |
CREATE EXTENSION postgis; 후 모든 geometry/geography 컬럼에는 거의 자동으로 GiST를 만들게 됩니다. PostGIS는 별도 책 한 권 분량이 필요해 본 문서는 PG 코어 인덱스에 집중.정리
- GiST = 공간·범위·KNN·overlap·containment에 강한 일반화 트리
- SP-GiST = 비겹침 분할 트리. point lookup·radix·prefix에서 GiST보다 빠를 수 있음
- EXCLUSION CONSTRAINT는 GiST의 강력한 사용 사례 — 동시에 겹치는 row를 DB에서 막음
btree_gist확장으로 평범한 타입을 GiST에 끼워 multi-key constraint 가능- 큰 테이블 인덱스 빌드는 CONCURRENTLY + buffering 권장
- KNN은 GiST 전용
다음 절(6.4)에서는 JSONB·full-text search·배열에 표준인 GIN 인덱스를 봅니다.